Integrating Amazon Echo and VMware API's

Integrating Amazon Echo and VMware API's

· by CodyDe · Read in about 19 min · (3877 words) ·

Start With the ending, a demo!

If a picture is worth a thousand words; a video is worth volumes! This is an incredibly long blog post; for those of you that wanted to just see it in action - Here's a demo video! For those of you that want to get into the details and figure out “how” we get here; read on after the video!

If you're interested in the code for this post, check out my GitHub repository that has this project in it.

This blog post would be REALLY long if I decided to cover all the topics I wanted to in one post. It's already really long as is! That being said; we aren't constrained by such things. We're going to break this up into several posts in order to make it a bit more digestible.

  • Part 1 - Build Python framework and simple vCenter API responses
  • Part 2 - Integrate vRealize Automation API's
  • Part 3 - Integrate with NSX API's
  • Part 4 - Hosting the Flask build on Apache

Talking about API's - Why does it matter?

You're a seasoned administrator. You've got your Web Console, you know the GUI like the back of your hand. Your PowerCLI game is strong. You're thinking; why do I even need API access to the platform? Isn't this just “cool words on paper”; with not a lot of real “production” use cases? This Echo blog post is cool; but why does it apply to me as an enterprise user?

API's enable programmatic access to platforms. What if you want to code your own customized reporting platform? What you have a slick NodeJS developer (or Flask, haters…) who wants to build a platform that interacts with VMware products independently? What if you want to create API driven automation without leveraging vRealize Orchestrator or vRealize Automation? It's not about “replacing” your existing skillsets; its about giving yourself another tool in your tool belt.

As a customer; I leveraged the API's heavily for building internal reporting platforms that were customized outside of VMware specific usecases. My incredibly skilled developer (@sgtstevo54) at my former employer would leverage API's between platforms, in conjunction with vRealize Orchestrator to build advanced XaaS blueprints in vRealize Automation that went outside of standard plugin functionality. He would create dynamic webapps that showed data or provided interaction points for various teams. The usecases started popping up of creating specific tools for helpdesk and operations teams to be able to perform critical tasks without leveraging administrative access via the Web Console.

Looking specifically at vRealize Automation; developers don't want to be restricted to leveraging a GUI. They want to be able to code their deployments into automated runs to deploy their environments and run their API driven “checks” against the environment. Even look at what we're doing internally; the entire vRealize Automation installation/upgrade processes are called via REST API between the endpoints.

You can access the REST API explorer for vCenter in 6.5 by heading to https://vcenterurl.domain.whatever/apiexplorer

There's also some great blog posts int he community around the topic that are worth checking out; linked below

Also make sure you are checking out the VMware{Code} community. There is so much content on here to read and learn about that I can't even begin to put into words, plus a TON of Slack channels for live interaction!

If we drop into the API Explorer at VMware{Code} there is a magic carpet ride of incredible information about the various API's you can leverage. All of these can be integrated with Amazon Echo after you learn the skills in this post!

Moving on…!

Getting started…What will we need?

Python is essentially going to be the backbone of everything we do in this guide. We'll be operating it pretty much exclusively; with only a few exceptions. You'll want to use the following commands to install Flask, Flask-Ask, requests, and configparser. We'll dig into all of these later; but for now…

pip install Flask
pip install Flask-Ask
pip install requests
pip install configparser

Flask is a microframework for building Python based web applications. There's a ton of great tutorials out there; and some major websites operate on Flask as a back end. It's a pretty incredible platform, and VERY easy to get started with. If you are interested in diving deep into Flask across the board, I highly recommend taking a look at Sentdex's tutorials over at PythonProgramming.net. His Flask series is pretty much what got me started with Python. He also has a great tutorial on building an Alexa skill using Flask-Ask that's a great view!

We'll be using Python and Flask in this context to create the web server that our Alexa skill will interact with. Flask-Ask is a Flask extension for building Alexa skills much quicker and easier.

If you want to segment your Python work from other work you might be doing in your environment, I recommend spinning up a separate VirtualEnvironment. There are plenty of guides out there for this; I won't be covering it here.

Enough Blabbing - Lets get started!

We need to start by setting up the basic Flask and Flask-Ask framework. If you're in a pinch for a great editor; I'd recommend leveraging Visual Studio Code. I'm a huge fan of PyCharm by JetBrains personally; but it costs $$$.

Let's create our first python file and populate it with some stuff…

# __init__.py - Starting of our application 

from flask import Flask
from flask_ask import Ask, statement, question

app = Flask(__name__)
ask = Ask(app, "/control_center")

if __name__ == '__main__':
    app.run(debug=True)

Lets take a quick look at whats happening here -

  • We import the flask module, as well as all the Flask-Ask components we're going to be using
  • We define the flask app, as well as the path for the app to start
  • We set the app to run with debug enabled

From here, if we run our python script (python init.py) our flask server will start up, and be listening by default on port 5000. Now, this is pretty useless in its current state; if we hit http://127.0.0.1:5000/control_center - we're going to get an error 405 (method is not supported). Lets drop a quick route into the init.py to correct this

@app.route('/')
def homepage():
    return "VMware Control Center Alexa Skill"

Now if we start our Flask app, and hit the root of our site, we'll at least get a return with the text above

For this, lets shift gears a bit and move into a new python file. We're going to use this file to hold all of our REST functions that we're going to define, as well as the authentication parameters for reaching out to our vCenter instance. We're going to use some tricks to do this in a clean way; leveraging the requests module, urllib3 module, as well as configparser to store our configurations. Lets do it!

Make a new file named whatever you want; I'm going to call mine vsphereapi.py. We'll start off with our 2 authentication functions. These functions came from a fantastic blog I discovered, who's owner I can't seem to locate in the twitter space. Thank you David Chidell; you got me started on this adventure!

I've slightly modified David's code to fit my own needs. The authentication functions (as well as the appropriate imports) we'll want to place in the vspehreapi.py file are below

import requests
import configparser
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)  # Disable SSL warnings

def auth_vcenter(username,password):
    print('Authenticating to vCenter, user: {}'.format(username))
    resp = requests.post('{}/com/vmware/cis/session'.format(url),auth=(user,password),verify=False)
    if resp.status_code != 200:
        print('Error! API responded with: {}'.format(resp.status_code))
        return
    return resp.json()['value']

def get_api_data(req_url):
    sid = auth_vcenter(user,password)
    print('Requesting Page: {}'.format(req_url))
    resp = requests.get(req_url,verify=False,headers={'vmware-api-session-id':sid})
    if resp.status_code != 200:
        print('Error! API responded with: {}'.format(resp.status_code))
        return
    return resp

First we import the requests module to handle making our HTTP requests. ConfigParser is going to be used to store our configuration details and the urllib3 module is what we are going to use to ignore any pesky cert errors we get from vCenter along the way.

After this, we have 2 functions - one for authentication to vCenter , returning a session ID (SID), and the other one to make the actual API call's using the previous function. David, in his blog post, mentions that this is probably inefficient in its current state. I'd agree - but it gets the job done for a lab environment and can be improved later.

In it's current state; this won't function. You can see we're missing some key configuration data; Username, Password, and the Base URL for the vCenter environment (the req_url variable in this case). We could hard code this into our code; but lets try to make this a little bit more secure by storing our configuration files in a separate file. Make a new file called config.txt (or .ini if you want to be cute…) and drop in some configuration data similar to below (matching your environment).

[vcenterConfig]
url = https://hlcorevc01.humblelab.com/rest
user = administrator@vsphere.local
password = VMware123!

We'll update our code to include the following snippet right below our urllib3 import. Note, you'll need to replace the value in config.read() with the path to your config file that you created; I left mine coded for my environment. Notice how on windows we use double \‘s between locations. This will obviously be different for Linux users.

config = configparser.ConfigParser()
config.read("C:\\Stuff\\Git\\AlexavSphere\\etc\\config.txt")
url = config.get("vcenterConfig","url")
user = config.get("vcenterConfig","user")
password = config.get("vcenterConfig","password")

This code snippet will “load” a configuration file, and pull in the values that are stored in that file to use in our functions. We'd apply some fine grained security controls around this file to ensure that only proper users were able to edit it, in a real world scenario.

While were making edits, lets drop 3 more “test” functions right below the ConfigParser info

def get_vcenter_health_status():
    health = get_api_data('{}/appliance/health/system'.format(url))
    j = health.json()
    return '{}'.format(j['value'])


def vm_count():
    countarry = []
    for i in get_api_data('{}/vcenter/vm'.format(url)).json()['value']:
        countarry.append(i['name'])
    p = len(countarry)
    return p

def get_cluster():
    cluster = get_api_data('{}/vcenter/host'.format(url))
    k = cluster.json()
    hosts = []
    for i in k['value']:
        hosts.append(i['name'])
    return hosts

You'll notice that within these functions we're pointing at a REST API path from vCenter. Our API functions will call these URL paths, and return JSON data back. We're going to use various python ninja skills to parse this data out into an Alexa digestible format and read it back vocally. Our vsphereapi.py script now looks like this…

import requests
import configparser
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)  # Disable SSL warnings

config = configparser.ConfigParser()
config.read("C:\\Stuff\\Git\\AlexavSphere\\etc\\config.txt")
url = config.get("vcenterConfig","url")
user = config.get("vcenterConfig","user")
password = config.get("vcenterConfig","password")

def get_vcenter_health_status():
    health = get_api_data('{}/appliance/health/system'.format(url))
    j = health.json()
    return '{}'.format(j['value'])


def vm_count():
    countarry = []
    for i in get_api_data('{}/vcenter/vm'.format(url)).json()['value']:
        countarry.append(i['name'])
    p = len(countarry)
    return p

def get_cluster():
    cluster = get_api_data('{}/vcenter/host'.format(url))
    k = cluster.json()
    hosts = []
    for i in k['value']:
        hosts.append(i['name'])
    return hosts

def auth_vcenter(username,password):
    print('Authenticating to vCenter, user: {}'.format(username))
    resp = requests.post('{}/com/vmware/cis/session'.format(url),auth=(user,password),verify=False)
    if resp.status_code != 200:
        print('Error! API responded with: {}'.format(resp.status_code))
        return
    return resp.json()['value']

def get_api_data(req_url):
    sid = auth_vcenter(user,password)
    print('Requesting Page: {}'.format(req_url))
    resp = requests.get(req_url,verify=False,headers={'vmware-api-session-id':sid})
    if resp.status_code != 200:
        print('Error! API responded with: {}'.format(resp.status_code))
        return
    return resp

Time to do some testing!

Save all your files. Lets do some testing! Go ahead and run python from your command prompt and copy your whole vsphereapi.py file and paste it into the python prompt. You may have to hit “enter” to complete the final line. Everything should accept in without a problem (Provided you've updated paths correctly, and set your variables correct for your environment). See the screenshot below -

Now, all of the functions we created have been loaded into our python shell. Lets run one to give it a shot! Go ahead and type in “get_cluster()” and hit return; watch the magic work!

We're returned with an array of our hosts within the clusters in our environment. Pretty Cool! Runining our other 2 test functions produces similar results

Perfect; everything is working!

We're done with vsphereapi.py and config.txt (or .ini) file. We can close them out; the rest of our work will be done on the init.py file to start building our Alexa communication mojo (intents and such).

Coding for Alexa Communication

We have 3 intents we need to build communication around within our Alexa skill.

  • Count VMs
  • Get Health of Appliance
  • Get Hosts in Clusters

We need to build the code for our Flask app to know how to call these intents. Let's revisit the top of our code and add the imports following

from vsphereapi import get_cluster, get_vcenter_health_status, count_vms

This code brings in our functions that we built in our vsphereapi.py script which will be used within our intents to return the data we want our Echo to vocally read back to us.

Earlier we added an “@app.route('/')” decorator with a function to give us at least a return when a user tried to hit our page. Below that, we'll drop the following deorator.

@ask.launch
def start_skill():
    welcome_message = 'v Sphere Control Center is online'
    return question(welcome_message)

This decorator will be what runs when the skill is launched using the “Alexa, Start Control Center” utterance. This is set during the skill build at the developer.amazon.com portal; but we need to have the code set to manage it here. This code block will effectively start our entire skill. An important item to note is that we are returning a question vs a statement. A question implies that an answer is following. If you want to be able to continue to “ask things”; use questions. If you want to report something and gracefully exit; use statements. This will become clearer as we move forward.

From here, we're going to bring in the intent decorators and subsequent code.

@ask.intent("VMCountIntent")
def share_count():
    counting = count_vms()
    count_msg = 'The total number of virtual machines in this v center is {}'.format(counting)
    return question(count_msg)

@ask.intent("HealthIntent")
def share_vcenter_health():
    health = get_vcenter_health_status()
    health_msg = 'The current health of the vCenter Appliance is {}'.format(health)
    return question(health_msg)

@ask.intent("HostClustersIntent")
def share_hosts_in_clusters():
    hosts = get_cluster()
    host_msg = 'Current hosts in clusters are {}'.format(hosts)

Now we've got some heavy lifting. We're leveraging the functions we brought in earlier to return messages that Alexa will read back to us. We're feeding these sentences in as variables using the .format method.

We'll need to build 1 more intent; a “NoIntent” that we will use to terminate the skill

@ask.intent("NoIntent")
def no_intent():
    bye_text = 'v Sphere Control Center Shutting Down'
    return statement(bye_text)

At this point, we've completed the Flask portion of our Alexa Skill work. If you've made it this far, you deserve a beer. Or a hug. Or at least a firm handshake and a congratulations.

Currently, our init.py should look like this…

# __init__.py - Starting of our application 
 
from flask import Flask
from flask_ask import Ask, statement, question
from vsphereapi import get_cluster, get_vcenter_health_status, count_vms
 
app = Flask(__name__)
ask = Ask(app, "/control_center")

@app.route('/')
def homepage():
    return "VMware Control Center Alexa Skill"

@ask.launch
def start_skill():
    welcome_message = 'v Sphere Control Center is online'
    return question(welcome_message)

@ask.intent("VMCountIntent")
def share_count():
    counting = count_vms()
    count_msg = 'The total number of virtual machines in this v center is {}'.format(counting)
    return question(count_msg)
 
@ask.intent("HealthIntent")
def share_vcenter_health():
    health = get_vcenter_health_status()
    health_msg = 'The current health of the vCenter Appliance is {}'.format(health)
    return question(health_msg)
 
@ask.intent("HostClustersIntent")
def share_hosts_in_clusters():
    hosts = get_cluster()
    host_msg = 'Current hosts in clusters are {}'.format(hosts)
    return question(host_msg)

if __name__ == '__main__':
    app.run(debug=True)

Save your code, and lets move along!

Where are we now? Time for Ngrok

If we start the Flask app right now (python init.py) it will start fine, and listen on http://127.0.0.1:5000 (or localhost:5000). We still need to setup Ngrok (setup is stretching it; it's a one liner) and then build our Alexa skill in the developer.amazon.com portal.

Hopefully you downloaded Ngrok earlier. We'll launch a command prompt in the Ngrok directory; and run the following command

ngrok http 5000

This will start the ngrok server, and present you with a screen similar to the following

It's important to note that the URL will change each time you use Ngrok, so as you're testing your skill - make sure you keep updating the URL in the Amazon portal. It should also be noted that its an ABSOLUTELY bad idea to keep this running all the time. For our purposes of setting this up and running it; it works great. Long term - the right things to do are -

  • Build a dedicated web server for your python code to run on (under Apache, most likely)
  • Setup SSL certs on that box, or setup a reverse proxy with SSL certs in front of it
  • Apply proper firewall rules (NSX?!) to segment off traffic to the systems.

Be smart. Do smart things.

Anyways, once we have Ngrok running; we'll copy down the HTTPS URL and save it off to the side; we're going to need it shortly!

Working in the Amazon Developer Console

Navigate over to https://developer.amazon.com and sign in/sign up using your credentials of choice.  Once in, select Alexa on the top, followed by Alexa Skills Kit.

We'll select “Add New Skill” on the upper right

We'll fill in our initial skills data, as pictured below. Note: pay special attention to the invocation name. This is what will start your application. If you want it be “Alexa, start HumbleLabRules” put it here. Other content can remain the default. Select save, and press next.

Now we are going to build what's called an intent schema. The intent schema tells Alexa what “intents” are possible. It's formatted as a JSON, drop this into the “Intent Schema” box

{
"intents": [
    {
      "intent": "NoIntent"
    },
    {
      "intent": "VMCountIntent"
    },
    {
      "intent": "HealthIntent"
    },
  	{
      "intent": "HostClustersIntent"
    }
  ]
}

You'll note that the intents we reference up here match the names of within the intent decorators that are within our init.py script? Imagine that!

Next, we build our utterances. Utterances tell Alexa what intents match which what phrases. You'll want to be careful here to list any possible combination you might use, but also to put things in the utterances phonetically (for example, v sphere instead of vsphere, etc…). Drop these values in the utterances box -

NoIntent abort
VMCountIntent report total vms
VMCountIntent count vms 
HealthIntent report health
HealthIntent report appliance health
HostClustersIntent list hosts in clusters
HostClustersIntent list hosts 

This is a good time to talk about the relationships between all the components we're working with. The best way to summarize it is like this - Ngrok exposes your Python Flask/Flask-ASK server to the internet, this is how the Amazon Skills Kit talks to your environment. Intents map to functions in Python. Utterances map vocal cues to intents.

Note: It's a really good idea to create an utterances.txt and an intents.txt to save with your code that you are building. It'll let you track the full scope of your Alexa app; and you can commit it to a code repository for appropriate tracking later on!

With these in place, hit next. Amazon will take a bit of time to build the interaction model. It'll move you to the next screen to continue your configuration while the model build happens.

On the configuration screen, select HTTPS under the Global Fields > Endpoint section. Select North America, and drop the Ngrok URL you saved earlier into the box - you'll also want to include the /control_center (or whatever you put in the ask = Ask(app, “name”) field earlier in your code) on the end of the url; like below, the rest can stay as default, select next

On the SSL certificate screen, since we are leveraging Ngrok and its certificates, we will use the “My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority” option as indicated in the screenshot I've attached

When you select next, your skill will be enabled in test mode. You can use the text based simulator for testing, or fire up your Alexa for conversation.

If you haven't yet, go ahead and fire up your Flask app by running “python init.py” from the command prompt. It should fire up without a hitch. Ngrok should still be running, and mapped to your localhost over port 5000. At this point, simply say “Alexa, start v Sphere Control Center” and she should respond with your welcome_message variable, in our case “v Sphere Control Center is online”

If you watch Ngrok, you'll see the API responses starting to fire as you use your intents. You'll also see errors when you say something wrong.

You'll also see responses on your python client based on what intents you call as well

Returning back to the developer portal…The remaining screens are fairly self explanatory and relate to publishing out your skill publicly. I'll leave it to you to decide if you want to publish your's out or not!

Wow…that was a lot…so what's next?!

This is by far my longest blog post so far; but it's also by far the coolest yet. You can really see how you can start with some basic API calls and get to a place where you are automating pretty extensive work using an Amazon Echo. When you start factoring in the concept of Alexa's “Slots” for inserting variables, things get really creative!

As far as this blog series goes, we still have quite a bit more ground to cover! This post was the longest; mostly because it was setting a foundation for the next 2 series items. The biggest thing was showing how working with REST API's is very easy to get started with. Leveraging the original Python SDK (which I blogged about previously…) was good; but for new users it was very confusing. The rest API's just make sense - and combined with the API explorer for vCenter (https://vcenterurl.domain.com/apiexplorer) you can start to see how you could extend your API's and start building new functions to consume!

In our next installment, we'll integrate our application with vRealize Automation so we can deploy blueprints using Alexa. From there we can get really creative about what sort of services we expose via XaaS, and enable via voice command! Finally, we'll take this entire application and put it on its own Apache web server; so we're no longer running it from our desktop.

Thanks for sticking with me this long! More to come soon!

Credit where credits due!

Flask - http://flask.pocoo.org/docs/0.12/

Flask-Ask - https://github.com/johnwheeler/flask-ask

Great Python Tutorials from Sentdex; Full video guides for everything as well as incredible Flask tutorials - https://pythonprogramming.net/

DailyHypervisor Video Showing an Example of vRealize Automation with Amazon Echo - https://dailyhypervisor.com/vrealize-voice-automation-amazon-echo-and-vra/

The Null Route - David Chidell's Blog with a great article on vCenter 6.5 REST API w/ Python - http://blog.dchidell.com/2016/11/23/vcenter-6-5-rest-api-w-python/

vmtocloud  - Ryan Kelly's blog on all things VMware, I used this a ton for learning how to use the REST API's for vRealize Automtion that will pop into the NEXT blog post - http://www.vmtocloud.com/