Skip to content

Tutorial 03 VMCP Responder

Ioannis Charalampidis edited this page Oct 25, 2014 · 4 revisions

In this tutorual we are going to set-up a VM Configuration Point (VMCP) server. This is a server the CernVM WebAPI extension is going to contact in order to obtain the details about the Virtual Machine you are about to launch.

Step 5 - Boilerplate

For the very first step we are going to need a webserver. The flask python mcroframework is the best for this case because with a few lines of code we can have a fully functional web server.

Create a new file called server.py and paste the following code:

from flask import Flask
from flask import request 
app = Flask(__name__)

@app.route("/")
def hello():
    return "This is a VMCP server!"

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

Save it and run it using python server.py. Open a web browser and visit http://test.local:5000/.

Congratulations! Your webserver is running!

Step 6 - Routing a VMCP response

A Virtual Machine Configuration is a JSON-encoded set of key/value parameters that define the characteristics of the Virtual Machine to be created. Such characteristics include the instance name, the operating system, the ammount of ram to allocate, the ammount of disk to allocate etc.

Let's prepare a dictionary with the configuration of our new VM:

# Machine configuration
MACHINE_CONFIG = {
    'name' : 'My first VM',
    'secret' : 'pr0t3ct_this',
    'userData' : "[amiconfig]\nplugins=cernvm\n[cernvm]\ncontextualization_key=0c41ec2627604bc09457de39e190c24c\n",
    'ram' : 128,
    'cpus' : 1,
    'disk' : 1024,
    'flags': 0x31
}

NOTE: If you are curious about the flags field, can have a look on Appendix.

Then let's create a new route on flask microframework that is going to render that response:

@app.route("/vmcp")
def vmcp_sign():
    return json.dumps( MACHINE_CONFIG )

Putting it all together:

import json
from flask import Flask
from flask import request 
app = Flask(__name__)

# Machine configuration
MACHINE_CONFIG = {
    'name' : 'My first VM',
    'secret' : 'pr0t3ct_this',
    'userData' : "[amiconfig]\nplugins=cernvm\n[cernvm]\nusers=user:users:user\n",
    'ram' : 128,
    'cpus' : 1,
    'disk' : 1024,
    'flags': 0x31
}

@app.route("/vmcp")
def vmcp_sign():
    return json.dumps( MACHINE_CONFIG )

@app.route("/")
def hello():
    return "This is a VMCP server!"

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

Step 7 - Signing the response

In order to form a proper VMCP response you will need to sign the response with your domain's private key. The tutorial files already contain a library for this purpose and a private key for the test.local domain, so let's use it!

First, we are going to need to import the utility library and instantiate a VMCPSigner class:

from util.vmcp import VMCPSigner

# Create a signer instance
signer = VMCPSigner( "res/test-local.pem" )

Now we can modify the vmcp_sign function in order to actually sign the response:

@app.route("/vmcp")
def vmcp_sign():
    return json.dumps( signer.sign( MACHINE_CONFIG, request.args.get('cvm_salt') ) )

Now, if you try and visit http://test.local:5000/vmcp you are going get an exception, because you are missing cvm_salt parameter. This parameter, along with cvm_hostid are passed as GET parameters to the VMCP URL.

The cvm_salt is a random string which takes part to the signing process and is used to prevent replay attacks.

If you want to test your set-up, just pass a random cvm_salt parameter: http://test.local:5000/vmcp?cvm_salt=random

Your code so far should look like this:

import json
from util.vmcp import VMCPSigner
from flask import Flask
from flask import request 
app = Flask(__name__)

# Create a signer instance
signer = VMCPSigner( "res/test-local.pem" )

# Machine configuration
MACHINE_CONFIG = {
    'name' : 'My first VM',
    'secret' : 'pr0t3ct_this',
    'userData' : "[amiconfig]\nplugins=cernvm\n[cernvm]\nusers=user:users:user\n",
    'ram' : 128,
    'cpus' : 1,
    'disk' : 1024,
    'flags': 0x31
}

@app.route("/vmcp")
def vmcp_sign():
    return json.dumps( signer.sign( MACHINE_CONFIG, request.args.get('cvm_salt') ) )

@app.route("/")
def hello():
    return "This is a VMCP server!"

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