-
Notifications
You must be signed in to change notification settings - Fork 3
Tutorial 05 Advanced WebAPI Example
In this final tutorial we are going to explore some more advanced features that CernVM WebAPI provides. We are going to explore some UI/UX optimizations and then the interface with the running VM.
For this tutorial we are going to start with the resulting index.html
from the previous one. You can find it as simple-completed.html
in the doc/tutorial
folder in the repository.
You may have noticed that there is a considerable delay between clicking Start and until the moment the VM is actually started. That's because there are various task taking place in the background. Therefore it's usually a good idea to show this progress to the end-users.
So let's add some additional HTML elements:
<div class="container">
<h1>Simple Example</h1>
<p>
<strong>Session state:</strong> <span id="lbl-status">Not started</span></p>
<p>
<button id="btn-start" class="btn btn-success">Start</button>
<button id="btn-stop" class="btn btn-danger">Stop</button>
</p>
<!-- Progress frame -->
<div id="progress-frame" class="well well-sm" style="display:none">
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;">0%</div>
</div>
<div><span id="lbl-progress"></span></div>
</div>
<!-- Alert frame -->
<div id="alert-frame" style="display:none" class="alert alert-danger">
<button onclick="$('#alert-frame').fadeOut()" type="button" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4>Oh snap! You got an error!</h4>
<p id="lbl-error">(None)</p>
</div>
</div>
To do so, you will need to listen for the started
, completed
, progress
and failed
events.
-
started
is fired when a lenghty task is about to begin. -
completed
is fired when the lengthy task is completed successfuly -
progress
is fired during the task, informing the user of it's actions -
failed
is fired when something went wrong and the request cannot be completed.
Such events are fired both by the plugin and the session objects. Therefore we are going to start with some generic handlers of such events:
// Helper function to update progress value
function update_progress_value(value) {
var percent = Number(value * 100).toFixed() + '%';
$("#progress-frame .progress-bar").css('width', percent);
$("#progress-frame .progress-bar").text(percent);
}
// Callback handler to show the progress bar
function progress_started(message) {
$("#progress-frame").show();
$("#lbl-progress").text(message);
update_progress_value(0.0);
}
// Callback handler to hide the progress bar when completed
function progress_completed(message) {
$("#progress-frame").hide();
$("#lbl-progress").text(message);
update_progress_value(1.0);
}
// Callback handler to update the progress bar
function progress_updated(message, value) {
$("#lbl-progress").text(message);
update_progress_value(value);
}
// Callback handler in case of error
function progress_error(message) {
$("#progress-frame").hide();
$("#lbl-error").text(message);
$('#alert-frame').fadeIn();
}
And now we can register these event handlers both on the plugin and session instances:
...
// Initialize CernVM WebAPI & Callback when ready
CVM.startCVMWebAPI(function(api) {
$("#lbl-status").text("CernVM WebAPI Ready");
// Bind progress listeners on the plugin instance
api.addEventListener('started', progress_started);
api.addEventListener('completed', progress_completed);
api.addEventListener('progress', progress_updated);
api.addEventListener('failed', progress_error);
// Request a session through our VMC endpoint
api.requestSession('http://test.local:5000/vmcp', function(session) {
$("#lbl-status").text("Session open");
window.s = session;
// Obtained from the Appendix
var STATE_NAMES = ['Not yet created', '', 'Powered off', 'Saved', 'Paused', 'Running'];
// Bind progress listeners on the session instance
session.addEventListener('started', progress_started);
session.addEventListener('completed', progress_completed);
session.addEventListener('progress', progress_updated);
session.addEventListener('failed', progress_error);
...
The CernVM WebAPI is not only reponsible for starting-up and controlling a Virtual Machine instance, but it's also the broker that enables interaction between the browser and the applications inside the guest.
Such interactions are performed through an HTTP channel called API Endpoint. By default that's the default webserver listening on port 80 inside the Virtual Machine, but this can be configured by the VMCP response.
The extension is constantly monitoring the status of the API Endpoint and when it changes state it fires the apiStateChanged
event.
Let's make a simple listener of such event that just show the status to the user. We are starting with some additional HTML elements:
<strong>Session state:</strong> <span id="lbl-status">Not started</span>
</p>
<!-- An additional label to show the API Status -->
<p>
<strong>API state:</strong> <a href="#" id="lbl-api-status">Not available</a>
</p>
<p>
<button id="btn-start" class="btn btn-success">Start</button>
We then handle the apiStateChanged
event from the session:
// Listen for API state changed events
session.addEventListener('apiStateChanged', function(newState, apiURL) {
if (newState) {
$("#lbl-api-status").text(apiURL);
$("#lbl-api-status").attr('href', apiURL);
} else {
$("#lbl-api-status").text("Not available");
$("#lbl-api-status").attr('href', "#");
}
});
Refresh your browser and wait a couple of minutes until your VM is fully booted. The moment the webserver is started the 'API state' will point to a URL through which you can access the webserver inside the VM.
Some times it might be desirable to configure some of the Virtual Machine parameters specified by the VMCP, after the user has already created the session. By default such modificataions are prohibited since they violate the security restrictions enforced by the VMCP approach. However its possible to define in your VMCP response a list of parameters that the user is allowed to modify.
In our example, open the setup.py
created in the third chapter of this tutorial and add a parameter canOverride
the MACHINE_CONFIG
like so:
# 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,
'canOverride': 'ram,cpus'
}
This parameter contains a comma-separated list of keys the user can modify by the .start()
function.
As a simple excercise try to:
- Add two sliders to the interface (one for the RAM and one for the number of CPUs your VM should have)
- Rewrite the start function call like this:
session.start({
'ram': ... ,
'cpus': ...
})