VMware NSX-T and Kubernetes Overview

As a Networking and Security Technical Account Specialist for VMware, I get a lot of questions regarding NSX and container integration with Kubernetes. Many network and security professionals are not aware of the underlying Kubernetes container architecture, services and communications paths, so before I start into how NSX works with containers, let’s examine why container development simplifies inter-process and application programming and how containers communicate via Kubernetes services.

The development of containers is driven greatly by the programmatic advantages it has versus server-based application development. A Kubernetes Pod is a group of one or more containers. Containers in a pod share an IP address, port space and have localhost communications within the pod. Containers also communicate with each other using standard inter-process communications like SystemV semaphores or POSIX shared memory space. These capabilities provide developers with much tighter and quicker development methods, and a large amount of abstraction from what they have to contend with in server-based application development.

Containers in a pod also share data volumes. Colocation (co-scheduling), shared fate, coordinated replication, resource sharing and dependency management are managed automatically in a pod.

One of the key internal Kubernetes services for NSX-T integration is the kube-proxy service. The kube-proxy service watches the Kubernetes master for the addition and removal of Service Endpoint objects. For each service, it creates an iptables rule that captures traffic to the Kubernetes Service’s back-end sets. Also, for each Endpoint object, it creates iptables rules which select a back-end Pod.

With NSX-T and the NSX Container Plugin (NCP), we leverage the NSX Kube-Proxy, which is a daemon running on each of the Kubernetes Nodes, which most refer to as “Minions” or “Workers”. It replaces the native distributed east-west load balancer in Kubernetes (kube-proxy & iptables) with Open vSwitch (OVS) load-balancing services.

Now that we’ve covered east-west communications in Kubernetes, I’ll address ingress and egress to Kubernetes clusters.

The Kubernetes Ingress is an API object that manages external access to the services cluster. By default, and in typical scenarios, Kubernetes services and pods have IPs only routable by the cluster network. All traffic destined for an edge router is dropped or forwarded elsewhere. An Ingress is a collection of rules that allow inbound connections to reach the cluster services.

Kubernetes Ingress can be configured to give services externally-reachable URLs, load-balance traffic, terminate SSL and offer name based hosting. The most common Open-source software used in non-NSX environments are Nginx and HAProxy, which many of you may be familiar with, supporting server-based application operations. There’s also an external load balancer object, not to be confused with the Ingress object.

When creating a Kubernetes service, you have the option to automatically create a cloud network load balancer. It provides an external-accessible IP address that forwards traffic to the correct port on the assigned minion / cluster node.

Now, let’s add NSX to the picture… When we install NSX-T in a Kubernetes environment, we replace the Kubernetes Ingress object with the NSX-T native layer-7 load balancer that performs these functions.

Since we’ve reviewed how traffic gets into a Kubernetes cluster, let’s take a look at how network security is handled.

A Kubernetes Network Policy is a security construct of how groups of pods are allowed to communicate among themselves and other network endpoints. Kubernetes Network Policies are implemented by the network plugin, so you must use a networking solution which supports NetworkPolicy, as simply creating the resource without a controller to implement it will not work. By default, Kubernetes pods are non-isolated and accept any traffic, from any source. Pods become isolated by having a Kubernetes Network Policy assigned to them. Namespaces that have a Network Policy or more assigned to them allow and reject any traffic per the policy.

And finally, let’s review NSX-T networking components for Kubernetes. As you can see in the graphic below, NSX-T components are deployed to support both the Kubernetes Master management network and the Kubernetes Minion nodes. This diagram depicts the use of a non-routable, “black-holed” network on a logical switch that is not connected to any logical router.

The Kubernetes Master management network and logical switch are uplinked to the NSX-T tier-0 router in the center of the diagram. The tier-0 router is also providing NAT to the Kubernetes Cluster. eBGP, being the only dynamic routing protocol supported by NSX-T at this time, will be configured with peer routes to the top or rack or even back to the core.

NSX tier-1 routers are instantiated in each Kubernetes Cluster Node and one is deployed for ingress services and load balancing we discussed previously.

For those who are unfamiliar with the difference between NSX-v Edges and NSX-T Edges see the “High-Level View of NSX-T Edge Within a Transport Zone” at docs.vmware.com. If you’re an NSX engineer or work with NSX as part of a team, I would highly recommend the VMware NSX-T: Install, Configure, Manage [V2.2] course. NSX-T is a major architecture change from NSX-v and there are too many changes in each component to even begin to list here. With that being said, in a simplistic view, NSX-T tier-0 routers serve as provider routers and NSX-T tier-1 routers serve as tenant routers. Each has different capabilities, so ensure to read up on features and the Service Router (SR) and Distributed Router (DR) components for a better understanding if needed.

Wrapping it up, Kubernetes with VMware NSX-T provides a much richer set of networking and security capabilities than are provided with native Kubernetes. It simplifies and provides automation and orchestration via a CMP or REST API, for K8s DevOps engineers and container developers alike. And to add that NSX-T is hybrid-cloud and multi-cloud capable, with greatly simplified networking and security, Kubernetes users should be very excited once they see what happens when they #runNSX.

 

 

Preparing your Mac for VMware NSX-v Python Development

PyNSXv is a high level python based library that exposes ready to use work-flows and a CLI tool that can be used to control VMware NSX for vSphere.

PyNSXv can be used in two different ways, as a library by importing the files in the /library subdirectory into your code, or as a CLI tool by executing pynsxv, from the command line after installation.

To install PyNSXv you’ll need to install or update MacOS Xcode, Python, Homebrew, plus some of the XML formating dependencies needed by PyNSXv and the underlying nsxramlclient.

1. Install Xcode and Python
xcode-select –install
curl https://bootstrap.pypa.io/get-pip.py > get-pip.py
sudo python get-pip.py

MacBook-Pro:~ erik$ xcode-select —install
xcode-select: note: install requested for command line developer tools
MacBook-Pro:~ erik$ curl https://bootstrap.pypa.io/get-pip.py > get-pip.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1622k  100 1622k    0     0  3468k      0 --:--:-- --:--:-- --:--:-- 3467k
MacBook-Pro:~ erik$ sudo python get-pip.py
Password:
Collecting pip
  Downloading https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 4.2MB/s 
Collecting wheel
  Downloading https://files.pythonhosted.org/packages/fc/e9/05316a1eec70c2bfc1c823a259546475bd7636ba6d27ec80575da523bc34/wheel-0.32.1-py2.py3-none-any.whl
Installing collected packages: pip, wheel
Successfully installed pip-18.1 wheel-0.32.1

2. Install Homebrew using Ruby
/usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”

MacBook-Pro:~ erik$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
==> This script will install:
/usr/local/bin/brew
/usr/local/share/doc/homebrew
/usr/local/share/man/man1/brew.1
/usr/local/share/zsh/site-functions/_brew
/usr/local/etc/bash_completion.d/brew
/usr/local/Homebrew
==> The following existing directories will be made group writable:
/usr/local/bin
/usr/local/share
/usr/local/share/man
/usr/local/share/man/man1
==> The following existing directories will have their owner set to erik:
/usr/local/bin
/usr/local/share
/usr/local/share/man
/usr/local/share/man/man1
==> The following existing directories will have their group set to admin:
/usr/local/bin
/usr/local/share
/usr/local/share/man
/usr/local/share/man/man1
==> The following new directories will be created:
/usr/local/etc
/usr/local/include
/usr/local/lib
/usr/local/sbin
/usr/local/var
/usr/local/opt
/usr/local/share/zsh
/usr/local/share/zsh/site-functions
/usr/local/var/homebrew
/usr/local/var/homebrew/linked
/usr/local/Cellar
/usr/local/Caskroom
/usr/local/Homebrew
/usr/local/Frameworks
Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/chmod u+rwx /usr/local/bin /usr/local/share /usr/local/share/man /usr/local/share/man/man1
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/bin /usr/local/share /usr/local/share/man /usr/local/share/man/man1
==> /usr/bin/sudo /usr/sbin/chown erik /usr/local/bin /usr/local/share /usr/local/share/man /usr/local/share/man/man1
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/bin /usr/local/share /usr/local/share/man /usr/local/share/man/man1
==> /usr/bin/sudo /bin/mkdir -p /usr/local/etc /usr/local/include /usr/local/lib /usr/local/sbin /usr/local/var /usr/local/opt /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var/homebrew /usr/local/var/homebrew/linked /usr/local/Cellar /usr/local/Caskroom /usr/local/Homebrew /usr/local/Frameworks
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/etc /usr/local/include /usr/local/lib /usr/local/sbin /usr/local/var /usr/local/opt /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var/homebrew /usr/local/var/homebrew/linked /usr/local/Cellar /usr/local/Caskroom /usr/local/Homebrew /usr/local/Frameworks
==> /usr/bin/sudo /bin/chmod 755 /usr/local/share/zsh /usr/local/share/zsh/site-functions
==> /usr/bin/sudo /usr/sbin/chown erik /usr/local/etc /usr/local/include /usr/local/lib /usr/local/sbin /usr/local/var /usr/local/opt /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var/homebrew /usr/local/var/homebrew/linked /usr/local/Cellar /usr/local/Caskroom /usr/local/Homebrew /usr/local/Frameworks
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/etc /usr/local/include /usr/local/lib /usr/local/sbin /usr/local/var /usr/local/opt /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var/homebrew /usr/local/var/homebrew/linked /usr/local/Cellar /usr/local/Caskroom /usr/local/Homebrew /usr/local/Frameworks
==> /usr/bin/sudo /bin/mkdir -p /Users/erik/Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Users/erik/Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown erik /Users/erik/Library/Caches/Homebrew
==> /usr/bin/sudo /bin/mkdir -p /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> /usr/bin/sudo /usr/sbin/chown erik /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 112581 (delta 21), reused 30 (delta 15), pack-reused 112539
Receiving objects: 100% (112581/112581), 25.80 MiB | 7.09 MiB/s, done.
Resolving deltas: 100% (82358/82358), done.
From https://github.com/Homebrew/brew
 * [new branch]          master     -> origin/master
 * [new tag]             0.1        -> 0.1
 * [new tag]             0.2        -> 0.2
 * [new tag]             0.3        -> 0.3
 * [new tag]             0.4        -> 0.4
 * [new tag]             0.5        -> 0.5
 * [new tag]             0.6        -> 0.6
 * [new tag]             0.7        -> 0.7
 * [new tag]             0.7.1      -> 0.7.1
 * [new tag]             0.8        -> 0.8
 * [new tag]             0.8.1      -> 0.8.1
 * [new tag]             0.9        -> 0.9
 * [new tag]             0.9.1      -> 0.9.1
 * [new tag]             0.9.2      -> 0.9.2
 * [new tag]             0.9.3      -> 0.9.3
 * [new tag]             0.9.4      -> 0.9.4
 * [new tag]             0.9.5      -> 0.9.5
 * [new tag]             0.9.8      -> 0.9.8
 * [new tag]             0.9.9      -> 0.9.9
 * [new tag]             1.0.0      -> 1.0.0
 * [new tag]             1.0.1      -> 1.0.1
 * [new tag]             1.0.2      -> 1.0.2
 * [new tag]             1.0.3      -> 1.0.3
 * [new tag]             1.0.4      -> 1.0.4
 * [new tag]             1.0.5      -> 1.0.5
 * [new tag]             1.0.6      -> 1.0.6
 * [new tag]             1.0.7      -> 1.0.7
 * [new tag]             1.0.8      -> 1.0.8
 * [new tag]             1.0.9      -> 1.0.9
 * [new tag]             1.1.0      -> 1.1.0
 * [new tag]             1.1.1      -> 1.1.1
 * [new tag]             1.1.10     -> 1.1.10
 * [new tag]             1.1.11     -> 1.1.11
 * [new tag]             1.1.12     -> 1.1.12
 * [new tag]             1.1.13     -> 1.1.13
 * [new tag]             1.1.2      -> 1.1.2
 * [new tag]             1.1.3      -> 1.1.3
 * [new tag]             1.1.4      -> 1.1.4
 * [new tag]             1.1.5      -> 1.1.5
 * [new tag]             1.1.6      -> 1.1.6
 * [new tag]             1.1.7      -> 1.1.7
 * [new tag]             1.1.8      -> 1.1.8
 * [new tag]             1.1.9      -> 1.1.9
 * [new tag]             1.2.0      -> 1.2.0
 * [new tag]             1.2.1      -> 1.2.1
 * [new tag]             1.2.2      -> 1.2.2
 * [new tag]             1.2.3      -> 1.2.3
 * [new tag]             1.2.4      -> 1.2.4
 * [new tag]             1.2.5      -> 1.2.5
 * [new tag]             1.2.6      -> 1.2.6
 * [new tag]             1.3.0      -> 1.3.0
 * [new tag]             1.3.1      -> 1.3.1
 * [new tag]             1.3.2      -> 1.3.2
 * [new tag]             1.3.3      -> 1.3.3
 * [new tag]             1.3.4      -> 1.3.4
 * [new tag]             1.3.5      -> 1.3.5
 * [new tag]             1.3.6      -> 1.3.6
 * [new tag]             1.3.7      -> 1.3.7
 * [new tag]             1.3.8      -> 1.3.8
 * [new tag]             1.3.9      -> 1.3.9
 * [new tag]             1.4.0      -> 1.4.0
 * [new tag]             1.4.1      -> 1.4.1
 * [new tag]             1.4.2      -> 1.4.2
 * [new tag]             1.4.3      -> 1.4.3
 * [new tag]             1.5.0      -> 1.5.0
 * [new tag]             1.5.1      -> 1.5.1
 * [new tag]             1.5.10     -> 1.5.10
 * [new tag]             1.5.11     -> 1.5.11
 * [new tag]             1.5.12     -> 1.5.12
 * [new tag]             1.5.13     -> 1.5.13
 * [new tag]             1.5.14     -> 1.5.14
 * [new tag]             1.5.2      -> 1.5.2
 * [new tag]             1.5.3      -> 1.5.3
 * [new tag]             1.5.4      -> 1.5.4
 * [new tag]             1.5.5      -> 1.5.5
 * [new tag]             1.5.6      -> 1.5.6
 * [new tag]             1.5.7      -> 1.5.7
 * [new tag]             1.5.8      -> 1.5.8
 * [new tag]             1.5.9      -> 1.5.9
 * [new tag]             1.6.0      -> 1.6.0
 * [new tag]             1.6.1      -> 1.6.1
 * [new tag]             1.6.10     -> 1.6.10
 * [new tag]             1.6.11     -> 1.6.11
 * [new tag]             1.6.12     -> 1.6.12
 * [new tag]             1.6.13     -> 1.6.13
 * [new tag]             1.6.14     -> 1.6.14
 * [new tag]             1.6.15     -> 1.6.15
 * [new tag]             1.6.16     -> 1.6.16
 * [new tag]             1.6.17     -> 1.6.17
 * [new tag]             1.6.2      -> 1.6.2
 * [new tag]             1.6.3      -> 1.6.3
 * [new tag]             1.6.4      -> 1.6.4
 * [new tag]             1.6.5      -> 1.6.5
 * [new tag]             1.6.6      -> 1.6.6
 * [new tag]             1.6.7      -> 1.6.7
 * [new tag]             1.6.8      -> 1.6.8
 * [new tag]             1.6.9      -> 1.6.9
 * [new tag]             1.7.0      -> 1.7.0
 * [new tag]             1.7.1      -> 1.7.1
 * [new tag]             1.7.2      -> 1.7.2
 * [new tag]             1.7.3      -> 1.7.3
 * [new tag]             1.7.4      -> 1.7.4
 * [new tag]             1.7.5      -> 1.7.5
 * [new tag]             1.7.6      -> 1.7.6
 * [new tag]             1.7.7      -> 1.7.7
HEAD is now at de9cb6920 Merge pull request #5123 from reitermarkus/test-locks
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
  https://github.com/Homebrew/brew#donations
==> Tapping homebrew/core
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core'...
remote: Enumerating objects: 4851, done.
remote: Counting objects: 100% (4851/4851), done.
remote: Compressing objects: 100% (4650/4650), done.
remote: Total 4851 (delta 55), reused 321 (delta 11), pack-reused 0
Receiving objects: 100% (4851/4851), 4.05 MiB | 2.65 MiB/s, done.
Resolving deltas: 100% (55/55), done.
Tapped 2 commands and 4637 formulae (4,893 files, 12.6MB).
==> Migrating /Library/Caches/Homebrew to /Users/erik/Library/Caches/Homebrew...
==> Deleting /Library/Caches/Homebrew...
Already up-to-date.
==> Installation successful!
==> Homebrew has enabled anonymous aggregate formulae and cask analytics.
Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics.html
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
  https://github.com/Homebrew/brew#donations

3. Install the XML formating dependencies needed by PyNSXv and the nsxramlclient
brew install libxml2
brew install libxslt
brew link libxml2 –force
brew link libxslt –force

MacBook-Pro:~ erik$ brew install libxml2
==> Installing dependencies for libxml2: gdbm, openssl, readline, sqlite and python@2
==> Installing libxml2 dependency: gdbm
==> Downloading https://homebrew.bintray.com/bottles/gdbm-1.18.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring gdbm-1.18.high_sierra.bottle.tar.gz
🍺  /usr/local/Cellar/gdbm/1.18: 20 files, 584.4KB
==> Installing libxml2 dependency: openssl
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2p.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring openssl-1.0.2p.high_sierra.bottle.tar.gz
==> Caveats
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
  /usr/local/etc/openssl/certs
and run
  /usr/local/opt/openssl/bin/c_rehash
openssl is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.
If you need to have openssl first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
For compilers to find openssl you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl/include"
==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2p: 1,793 files, 12.3MB
==> Installing libxml2 dependency: readline
==> Downloading https://homebrew.bintray.com/bottles/readline-7.0.5.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring readline-7.0.5.high_sierra.bottle.tar.gz
==> Caveats
readline is keg-only, which means it was not symlinked into /usr/local,
because macOS provides the BSD libedit library, which shadows libreadline.
In order to prevent conflicts when programs look for libreadline we are
defaulting this GNU Readline installation to keg-only.
For compilers to find readline you may need to set:
  export LDFLAGS="-L/usr/local/opt/readline/lib"
  export CPPFLAGS="-I/usr/local/opt/readline/include"
==> Summary
🍺  /usr/local/Cellar/readline/7.0.5: 46 files, 1.5MB
==> Installing libxml2 dependency: sqlite
==> Downloading https://homebrew.bintray.com/bottles/sqlite-3.25.2.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring sqlite-3.25.2.high_sierra.bottle.tar.gz
==> Caveats
sqlite is keg-only, which means it was not symlinked into /usr/local,
because macOS provides an older sqlite3.
If you need to have sqlite first in your PATH run:
  echo 'export PATH="/usr/local/opt/sqlite/bin:$PATH"' >> ~/.bash_profile
For compilers to find sqlite you may need to set:
  export LDFLAGS="-L/usr/local/opt/sqlite/lib"
  export CPPFLAGS="-I/usr/local/opt/sqlite/include"
==> Summary
🍺  /usr/local/Cellar/sqlite/3.25.2: 11 files, 3.7MB
==> Installing libxml2 dependency: python@2
==> Downloading https://homebrew.bintray.com/bottles/python@2-2.7.15_1.high_sierra.bottle.6.tar.gz
######################################################################## 100.0%
==> Pouring python@2-2.7.15_1.high_sierra.bottle.6.tar.gz
==> /usr/local/Cellar/python@2/2.7.15_1/bin/python -s setup.py --no-user-cfg install --force --verbose --single-version-externally
==> /usr/local/Cellar/python@2/2.7.15_1/bin/python -s setup.py --no-user-cfg install --force --verbose --single-version-externally
==> /usr/local/Cellar/python@2/2.7.15_1/bin/python -s setup.py --no-user-cfg install --force --verbose --single-version-externally
==> Caveats
Pip and setuptools have been installed. To update them
  pip install --upgrade pip setuptools
You can install Python packages with
  pip install <package>
They will install into the site-package directory
  /usr/local/lib/python2.7/site-packages
See: https://docs.brew.sh/Homebrew-and-Python
==> Summary
🍺  /usr/local/Cellar/python@2/2.7.15_1: 4,665 files, 82.6MB
==> Installing libxml2
==> Downloading https://homebrew.bintray.com/bottles/libxml2-2.9.7.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libxml2-2.9.7.high_sierra.bottle.tar.gz
==> Caveats
libxml2 is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have libxml2 first in your PATH run:
  echo 'export PATH="/usr/local/opt/libxml2/bin:$PATH"' >> ~/.bash_profile
For compilers to find libxml2 you may need to set:
  export LDFLAGS="-L/usr/local/opt/libxml2/lib"
  export CPPFLAGS="-I/usr/local/opt/libxml2/include"
==> Summary
🍺  /usr/local/Cellar/libxml2/2.9.7: 281 files, 10.4MB
==> Caveats
==> openssl
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
  /usr/local/etc/openssl/certs
and run
  /usr/local/opt/openssl/bin/c_rehash
openssl is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.
If you need to have openssl first in your PATH run:
  echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
For compilers to find openssl you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl/include"
==> readline
readline is keg-only, which means it was not symlinked into /usr/local,
because macOS provides the BSD libedit library, which shadows libreadline.
In order to prevent conflicts when programs look for libreadline we are
defaulting this GNU Readline installation to keg-only.
For compilers to find readline you may need to set:
  export LDFLAGS="-L/usr/local/opt/readline/lib"
  export CPPFLAGS="-I/usr/local/opt/readline/include"
==> sqlite
sqlite is keg-only, which means it was not symlinked into /usr/local,
because macOS provides an older sqlite3.
If you need to have sqlite first in your PATH run:
  echo 'export PATH="/usr/local/opt/sqlite/bin:$PATH"' >> ~/.bash_profile
For compilers to find sqlite you may need to set:
  export LDFLAGS="-L/usr/local/opt/sqlite/lib"
  export CPPFLAGS="-I/usr/local/opt/sqlite/include"
==> python@2
Pip and setuptools have been installed. To update them
  pip install --upgrade pip setuptools
You can install Python packages with
  pip install <package>
They will install into the site-package directory
  /usr/local/lib/python2.7/site-packages
See: https://docs.brew.sh/Homebrew-and-Python
==> libxml2
libxml2 is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have libxml2 first in your PATH run:
  echo 'export PATH="/usr/local/opt/libxml2/bin:$PATH"' >> ~/.bash_profile
For compilers to find libxml2 you may need to set:
  export LDFLAGS="-L/usr/local/opt/libxml2/lib"
  export CPPFLAGS="-I/usr/local/opt/libxml2/include"
MacBook-Pro:~ erik$ brew install libxslt
==> Downloading https://homebrew.bintray.com/bottles/libxslt-1.1.32.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libxslt-1.1.32.high_sierra.bottle.tar.gz
==> Caveats
To allow the nokogiri gem to link against this libxslt run:
  gem install nokogiri -- --with-xslt-dir=/usr/local/opt/libxslt
libxslt is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have libxslt first in your PATH run:
  echo 'export PATH="/usr/local/opt/libxslt/bin:$PATH"' >> ~/.bash_profile
For compilers to find libxslt you may need to set:
  export LDFLAGS="-L/usr/local/opt/libxslt/lib"
  export CPPFLAGS="-I/usr/local/opt/libxslt/include"
==> Summary
🍺  /usr/local/Cellar/libxslt/1.1.32: 148 files, 3MB
MacBook-Pro:~ erik$ brew link libxml2 --force
Linking /usr/local/Cellar/libxml2/2.9.7... 21 symlinks created
If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/libxml2/bin:$PATH"' >> ~/.bash_profile
MacBook-Pro:~ erik$ brew link libxslt --force
Linking /usr/local/Cellar/libxslt/1.1.32... 22 symlinks created
If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/libxslt/bin:$PATH"' >> ~/.bash_profile
MacBook-Pro:~ erik$ echo 'export PATH="/usr/local/opt/libxslt/bin:$PATH"' >> ~/.bash_profile

4. Install PyNSXv
sudo pip install pynsxv

MacBook-Pro:~ erik$ sudo pip install pynsxv
Password:
The directory '/Users/erik/Library/Caches/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/Users/erik/Library/Caches/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting pynsxv
  Downloading https://files.pythonhosted.org/packages/9e/a4/231aed804e77bbd7825f74a129879b600d2316926e1ecc31fa295b673ab4/PyNSXv-0.4.1.tar.gz (137kB)
    100% |████████████████████████████████| 143kB 2.3MB/s 
Collecting nsxramlclient>=2.0.1 (from pynsxv)
  Downloading https://files.pythonhosted.org/packages/b3/da/bda98b9ce1f8d52a34708c75bfdd1dd8076e6c59a020d9173e682e9196af/nsxramlclient-2.0.7.tar.gz
Collecting pyvmomi (from pynsxv)
  Downloading https://files.pythonhosted.org/packages/b2/0c/6d6772be8df97d0965a1531612035f883a16dae5a892c835687507f683ac/pyvmomi-6.7.0.2018.9.tar.gz (262kB)
    100% |████████████████████████████████| 266kB 2.2MB/s 
Collecting tabulate (from pynsxv)
  Downloading https://files.pythonhosted.org/packages/12/c2/11d6845db5edf1295bc08b2f488cf5937806586afe42936c3f34c097ebdc/tabulate-0.8.2.tar.gz (45kB)
    100% |████████████████████████████████| 51kB 8.3MB/s 
Collecting pyopenssl (from nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/96/af/9d29e6bd40823061aea2e0574ccb2fcf72bfd6130ce53d32773ec375458c/pyOpenSSL-18.0.0-py2.py3-none-any.whl (53kB)
    100% |████████████████████████████████| 61kB 10.8MB/s 
Collecting pyraml-parser>=0.1.3 (from nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/9b/82/58f8e545845fa56cd05c6a28f604151252276d45d0ab6df3ba5527e61cfe/pyraml-parser-0.1.7.tar.gz
Collecting lxml&lt;=3.6.0 (from nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/11/1b/fe6904151b37a0d6da6e60c13583945f8ce3eae8ebd0ec763ce546358947/lxml-3.6.0.tar.gz (3.7MB)
    100% |████████████████████████████████| 3.7MB 2.3MB/s 
Collecting requests>=2.7.0 (from nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/65/47/7e02164a2a3db50ed6d8a6ab1d6d60b69c4c3fdf57a284257925dfc12bda/requests-2.19.1-py2.py3-none-any.whl (91kB)
    100% |████████████████████████████████| 92kB 2.4MB/s 
Collecting six>=1.7.3 (from pyvmomi->pynsxv)
  Downloading https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl
Collecting cryptography>=2.2.1 (from pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/5d/b1/9863611b121ee524135bc0068533e6d238cc837337170e722224fe940e2d/cryptography-2.3.1-cp27-cp27m-macosx_10_6_intel.whl (1.5MB)
    100% |████████████████████████████████| 1.5MB 2.9MB/s 
Requirement already satisfied: setuptools in /usr/local/lib/python2.7/site-packages (from pyraml-parser>=0.1.3->nsxramlclient>=2.0.1->pynsxv) (40.4.3)
Collecting PyYAML>=3.10 (from pyraml-parser>=0.1.3->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz (270kB)
    100% |████████████████████████████████| 276kB 3.5MB/s 
Collecting certifi>=2017.4.17 (from requests>=2.7.0->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/56/9d/1d02dd80bc4cd955f98980f28c5ee2200e1209292d5f9e9cc8d030d18655/certifi-2018.10.15-py2.py3-none-any.whl (146kB)
    100% |████████████████████████████████| 153kB 3.6MB/s 
Collecting urllib3&lt;1.24,>=1.21.1 (from requests>=2.7.0->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 3.8MB/s 
Collecting chardet&lt;3.1.0,>=3.0.2 (from requests>=2.7.0->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 3.7MB/s 
Collecting idna&lt;2.8,>=2.5 (from requests>=2.7.0->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 10.5MB/s 
Collecting cffi!=1.11.3,>=1.7 (from cryptography>=2.2.1->pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/7e/4a/b647e46faaa2dcfb16069b6aad2d8509982fd63710a325b8ad7db80f18be/cffi-1.11.5-cp27-cp27m-macosx_10_6_intel.whl (238kB)
    100% |████████████████████████████████| 245kB 4.2MB/s 
Collecting enum34; python_version &lt; "3" (from cryptography>=2.2.1->pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/c5/db/e56e6b4bbac7c4a06de1c50de6fe1ef3810018ae11732a50f15f62c7d050/enum34-1.1.6-py2-none-any.whl
Collecting asn1crypto>=0.21.0 (from cryptography>=2.2.1->pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd8ab5ed7cdc600ef7cd06222/asn1crypto-0.24.0-py2.py3-none-any.whl (101kB)
    100% |████████████████████████████████| 102kB 4.4MB/s 
Collecting ipaddress; python_version &lt; "3" (from cryptography>=2.2.1->pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/fc/d0/7fc3a811e011d4b388be48a0e381db8d990042df54aa4ef4599a31d39853/ipaddress-1.0.22-py2.py3-none-any.whl
Collecting pycparser (from cffi!=1.11.3,>=1.7->cryptography>=2.2.1->pyopenssl->nsxramlclient>=2.0.1->pynsxv)
  Downloading https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz (158kB)
    100% |████████████████████████████████| 163kB 3.8MB/s 
Installing collected packages: six, pycparser, cffi, enum34, idna, asn1crypto, ipaddress, cryptography, pyopenssl, PyYAML, pyraml-parser, lxml, certifi, urllib3, chardet, requests, nsxramlclient, pyvmomi, tabulate, pynsxv
  Running setup.py install for pycparser ... done
  Running setup.py install for PyYAML ... done
  Running setup.py install for pyraml-parser ... done
  Running setup.py install for lxml ... done
  Running setup.py install for nsxramlclient ... done
  Running setup.py install for pyvmomi ... done
  Running setup.py install for tabulate ... done
  Running setup.py install for pynsxv ... done
Successfully installed PyYAML-3.13 asn1crypto-0.24.0 certifi-2018.10.15 cffi-1.11.5 chardet-3.0.4 cryptography-2.3.1 enum34-1.1.6 idna-2.7 ipaddress-1.0.22 lxml-3.6.0 nsxramlclient-2.0.7 pycparser-2.19 pynsxv-0.4.1 pyopenssl-18.0.0 pyraml-parser-0.1.7 pyvmomi-6.7.0.2018.9 requests-2.19.1 six-1.11.0 tabulate-0.8.2 urllib3-1.23

5. Using PyNSXv as a CLI Tool
After you installed PyNSXv, the first thing you have to do is to create an .ini file that contains the host names and credentials of your vCenter and NSX Manager.

If you have multiple NSX environments, you can create multiple .ini files and reference them using the -i command line option. By default PyNSXv will look for a file called nsx.ini in the path you are running the pynsxv command in.

# [nsxraml]

<h1>nsxraml_file = &lt;></h1>

<h1>Uncomment the above section and add the path to the raml spec you want to use instead of the bundled version</h1>

[nsxv]
nsx_manager = <nsx_manager_IP>
nsx_username = admin
nsx_password = <nsx_manager_password>

[vcenter]
vcenter = <VC_IP_or_Hostname>
vcenter_user = administrator@domain.local
vcenter_passwd = <vc_password>

[defaults]
transport_zone = <transport_zone_name>
datacenter_name = <vcenter datacenter name>
edge_datastore = <datastore name to deploy edges in>
edge_cluster = <vcenter cluster for edge gateways>

After placing the nsx.ini file in you path, you can run pynsxv from your shell or cmd prompt.

pynsxv -h
usage: pynsxv [-h] [-i INI] [-v] [-d] {lswitch,dlr,esg,dhcp,dfw,usage} ...

PyNSXv Command Line Client for NSX for vSphere

positional arguments:
  {lswitch,dlr,esg,dhcp,dfw,usage}
    lswitch             Functions for logical switches
    dlr                 Functions for distributed logical routers
    esg                 Functions for edge services gateways
    dhcp                Functions for Edge DHCP
    dfw                 Functions for distributed firewall
    usage               Functions to retrieve NSX-v usage statistics

optional arguments:
  -h, --help            show this help message and exit
  -i INI, --ini INI     nsx configuration file
  -v, --verbose         increase output verbosity
  -d, --debug           print low level debug of http transactions

Here is an example output using the lswitch subcommand

pynsxv lswitch list
+---------------------+----------------+
| LS name             | LS ID          |
|---------------------+----------------|
| edge_ls             | virtualwire-63 |
| dlr_ls              | virtualwire-64 |
+---------------------+----------------+

Each subcommand has its own set of subcommands, as well as arguments.
You can see what is available by using -h after the first subcommand.

pynsxv lswitch -h
usage: cli.py lswitch [-h] [-t TRANSPORT_ZONE] [-n NAME] command

Functions for logical switches

positional arguments:
  command
                            create: create a new logical switch
                            read:   return the virtual wire id of a logical switch
                            delete: delete a logical switch"
                            list:   return a list of all logical switches

optional arguments:
  -h, --help            show this help message and exit
  -t TRANSPORT_ZONE, --transport_zone TRANSPORT_ZONE
                        nsx transport zone
  -n NAME, --name NAME  logical switch name, needed for create, read and delete

You can also use the -v switch of the main pynsxv command to switch to a json formated output for use with shell scripts, which I’ll detail in my next post. How to Export/Import VMware NSX Distributed Firewall Rules, which is great way to migrate working lab DFW rulesets into production. …stay tuned for more!