Commit ef9c1b4f authored by Stavros Sachtouris's avatar Stavros Sachtouris

Implement contextualization

When a new key is registered, it is registered on req.environ in a
dict called 'soi:public_keys'.
The snf_create_server method pulls the keys from there and handles
the way they are injected into the VM.
parent b1df0793
......@@ -12,8 +12,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <>.
from utils import empty_list_200
from base64 import b64encode
import webob.exc
from soi.utils import empty_list_200
from os.path import join
def snf_index(cls, req):
......@@ -116,19 +118,49 @@ def snf_get_server_net_attachments(cls, req, compute_id):
return r
def _get_personality(image, public_key):
"""Resolve superuser from VM, prepare public_key injection
:returns: {contents: public_key, path: , owner: , group: , mode: }
pkey = b64encode(public_key)
personality = [{
'contents': pkey,
'path': '/var/lib/cloud/seed/nocloud-net/meta-data'
users = image['metadata']['users']
except KeyError:
return personality
for user in users.split():
prefix = '/' if user == 'root' else '/home'
path = join(prefix, user, '.ssh', 'authorized_keys')
contents=pkey, path=path, owner=user, group=user, mode=0600))
return personality
def snf_create_server(cls, req, name, image, flavor, **kwargs):
"""Synnefo: create a new VM"""
req.environ['service_type'] = 'compute'
req.environ['method_name'] = 'servers_post'
project = req.environ.get('HTTP_X_PROJECT_ID', None)
body = dict(name=name, imageRef=image, flavorRef=flavor, project=project)
public_keys = req.environ.get('soi:public_keys')
if public_keys:
image_info, personality = snf_get_image(cls, req, image), []
for public_key in public_keys.values():
personality += _get_personality(image_info, public_key)
body['personality'] = personality
req.environ['kwargs'] = dict(json_data=dict(server=body))
req.environ['service_type'] = 'compute'
req.environ['method_name'] = 'servers_post'
response = req.get_response(
r = cls.get_from_response(response, 'server', {})
_openstackify_addresses(r['addresses'], r['attachments'])
return r
......@@ -158,6 +190,13 @@ def snf_run_action(cls, req, action, server_id):
def keypair_register(cls, req, name, public_key):
"""Put public key in req.environ['public_keys'], with name as key"""
public_keys = req.environ.get('soi:public_keys', {})
public_keys[name] = public_key
req.environ['soi:public_keys'] = public_keys
function_map = {
'index': snf_index,
'get_server': snf_get_server,
......@@ -171,4 +210,5 @@ function_map = {
'delete': snf_delete_server,
'create_server': snf_create_server,
'run_action': snf_run_action,
'keypair_create': keypair_register
......@@ -15,6 +15,7 @@
from soi.tests import fakes
from soi import compute
from mock import patch
from base64 import b64encode
@patch('soi.tests.fakes.DummyClass.get_from_response', return_value='g f r')
......@@ -277,6 +278,79 @@ def test_snf_create_server(gr, gfr, _oa):
_response['addresses'], _response['attachments'])
def test__get_personality():
"""Test personality syntax method"""
image = dict(id='some id', metadata=dict(users='root'))
public_key = 'some public key'
pkey = b64encode(public_key)
constant = '/var/lib/cloud/seed/nocloud-net/meta-data'
suffix = '.ssh/authorized_keys'
r = compute._get_personality(image, public_key)
assert r == [
{'contents': pkey, 'path': constant},
'contents': pkey, 'path': '/root/{0}'.format(suffix),
'owner': 'root', 'group': 'root', 'mode': 0600
image['metadata']['users'] = 'user'
r = compute._get_personality(image, public_key)
assert r == [
{'contents': pkey, 'path': constant},
'contents': pkey, 'path': '/home/user/{0}'.format(suffix),
'owner': 'user', 'group': 'user', 'mode': 0600
image['metadata']['users'] = 'root user'
r = compute._get_personality(image, public_key)
assert r == [
{'contents': pkey, 'path': constant},
'contents': pkey, 'path': '/root/{0}'.format(suffix),
'owner': 'root', 'group': 'root', 'mode': 0600
'contents': pkey, 'path': '/home/user/{0}'.format(suffix),
'owner': 'user', 'group': 'user', 'mode': 0600
@patch('soi.compute.snf_get_image', return_value='some image')
@patch('soi.compute._get_personality', return_value=['some key'])
@patch('soi.tests.fakes.DummyClass.get_from_response', return_value=_response)
@patch('soi.tests.fakes.FakeReq.get_response', return_value='my response')
def test_snf_create_server_with_pk(gr, gfr, _oa, gp, snfci):
"""Test snf_create_server"""
cls, req = fakes.DummyClass(), fakes.FakeReq()
req.environ['soi:public_keys'] = {'key_name': 'some key'}
req.environ['HTTP_X_PROJECT_ID'] = 'a project id'
args = ('a name', 'an image', 'a flavor')
r = compute.snf_create_server(cls, req, *args)
assert r == _response
import json
print json.dumps(req.environ, indent=2)
assert req.environ == {
'HTTP_X_PROJECT_ID': 'a project id',
'service_type': 'compute',
'method_name': 'servers_post',
'soi:public_keys': {'key_name': 'some key'},
'kwargs': dict(json_data=dict(server=dict(
name='a name', imageRef='an image', flavorRef='a flavor',
project='a project id', personality=['some key', ])))}
gfr.assert_called_once_with('my response', 'server', {})
_response['addresses'], _response['attachments'])
def test_snf_delete_server(gr):
"""Test snf_delete_server"""
......@@ -326,3 +400,16 @@ def test_snf_run_action_suspend(gr):
except Exception as e:
from webob.exc import HTTPNotImplemented
assert isinstance(e, HTTPNotImplemented)
def test_keypair_register():
"""Test keypair_register"""
cls, req = fakes.DummyClass(), fakes.FakeReq()
name, pk = 'key_name', 'rsa-ssa keystuffhere ==user@host'
compute.keypair_register(cls, req, name, pk)
assert 'soi:public_keys' in req.environ
assert req.environ['soi:public_keys'] == {name: pk}
name0, pk0 = 'key_name0', 'rsa-ssa otherstuffhere ==user@host'
compute.keypair_register(cls, req, name0, pk0)
assert req.environ['soi:public_keys'] == {name: pk, name0: pk0}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment