OCCI 1.2 Contextualization

Introduces two new mixins defined in the OCCI 1.2 spec for handling
contextualization: user_data and ssh_key. Functionality is similar to
the already existing OpenStack mixins, which may be deprecated in the
future.

Partially-Implements: blueprint occi-1-2-core
Change-Id: Ibb6624e3e15bf7b9f3ad39786dcefb36b1a9a797
This commit is contained in:
Enol Fernandez 2016-11-11 13:44:17 +00:00
parent db9dae4a26
commit e30fdd75f4
9 changed files with 339 additions and 35 deletions

View File

@ -21,11 +21,12 @@ import ooi.api.helpers
from ooi import exception from ooi import exception
from ooi.occi.core import collection from ooi.occi.core import collection
from ooi.occi.infrastructure import compute from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import contextualization
from ooi.occi.infrastructure import network from ooi.occi.infrastructure import network
from ooi.occi.infrastructure import storage from ooi.occi.infrastructure import storage
from ooi.occi.infrastructure import storage_link from ooi.occi.infrastructure import storage_link
from ooi.occi import validator as occi_validator from ooi.occi import validator as occi_validator
from ooi.openstack import contextualization from ooi.openstack import contextualization as os_contextualization
from ooi.openstack import helpers from ooi.openstack import helpers
from ooi.openstack import network as os_network from ooi.openstack import network as os_network
from ooi.openstack import templates from ooi.openstack import templates
@ -148,7 +149,9 @@ class Controller(ooi.api.base.Controller):
], ],
"optional_mixins": [ "optional_mixins": [
contextualization.user_data, contextualization.user_data,
contextualization.public_key, contextualization.ssh_key,
os_contextualization.user_data,
os_contextualization.public_key,
], ],
"optional_links": [ "optional_links": [
storage.StorageResource.kind, storage.StorageResource.kind,
@ -166,26 +169,45 @@ class Controller(ooi.api.base.Controller):
flavor = obj["schemes"][templates.OpenStackResourceTemplate.scheme][0] flavor = obj["schemes"][templates.OpenStackResourceTemplate.scheme][0]
user_data, key_name, key_data = None, None, None user_data, key_name, key_data = None, None, None
create_key, create_key_tmp = False, False create_key, create_key_tmp = False, False
if contextualization.user_data.scheme in obj["schemes"]: # TODO(enolfc): deprecate OS Contextualization in the future
# meanwhile, raise 409 conflict if both contextualizations types appear
if (os_contextualization.user_data.scheme in obj["schemes"] and
contextualization.user_data.scheme in obj["schemes"]):
raise exception.OCCIMixinConflict()
if os_contextualization.user_data.scheme in obj["schemes"]:
user_data = attrs.get("org.openstack.compute.user_data") user_data = attrs.get("org.openstack.compute.user_data")
if contextualization.public_key.scheme in obj["schemes"]: if contextualization.user_data.scheme in obj["schemes"]:
user_data = attrs.get("occi.compute.user_data")
if (os_contextualization.public_key.scheme in obj["schemes"] and
contextualization.ssh_key.scheme in obj["schemes"]):
raise exception.OCCIMixinConflict()
key_name = key_data = None
if os_contextualization.public_key.scheme in obj["schemes"]:
key_name = attrs.get("org.openstack.credentials.publickey.name") key_name = attrs.get("org.openstack.credentials.publickey.name")
key_data = attrs.get("org.openstack.credentials.publickey.data") key_data = attrs.get("org.openstack.credentials.publickey.data")
if key_name and key_data: if contextualization.ssh_key.scheme in obj["schemes"]:
create_key = True if key_data or key_name:
elif not key_name and key_data: raise exception.OCCIMixinConflict()
# NOTE(orviz) To be occi-os compliant, not key_data = attrs.get("occi.credentials.ssh_key")
# raise exception.MissingKeypairName
key_name = uuid.uuid4().hex
create_key = True
create_key_tmp = True
if create_key: if key_name and key_data:
# add keypair: if key_name already exists, a 409 HTTP code create_key = True
# will be returned by OpenStack elif not key_name and key_data:
self.os_helper.keypair_create(req, key_name, # NOTE(orviz) To be occi-os compliant, not
public_key=key_data) # raise exception.MissingKeypairName
key_name = uuid.uuid4().hex
create_key = True
create_key_tmp = True
if create_key:
# add keypair: if key_name already exists, a 409 HTTP code
# will be returned by OpenStack
self.os_helper.keypair_create(req, key_name,
public_key=key_data)
block_device_mapping_v2 = self._build_block_mapping(req, obj) block_device_mapping_v2 = self._build_block_mapping(req, obj)
networks = self._get_network_from_req(req, obj) networks = self._get_network_from_req(req, obj)

View File

@ -20,12 +20,13 @@ from ooi.occi.core import entity
from ooi.occi.core import link from ooi.occi.core import link
from ooi.occi.core import resource from ooi.occi.core import resource
from ooi.occi.infrastructure import compute from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import contextualization
from ooi.occi.infrastructure import network from ooi.occi.infrastructure import network
from ooi.occi.infrastructure import network_link from ooi.occi.infrastructure import network_link
from ooi.occi.infrastructure import storage from ooi.occi.infrastructure import storage
from ooi.occi.infrastructure import storage_link from ooi.occi.infrastructure import storage_link
from ooi.occi.infrastructure import templates as infra_templates from ooi.occi.infrastructure import templates as infra_templates
from ooi.openstack import contextualization from ooi.openstack import contextualization as os_contextualization
from ooi.openstack import network as os_network from ooi.openstack import network as os_network
from ooi.openstack import templates from ooi.openstack import templates
@ -108,8 +109,12 @@ class Controller(base.Controller):
mixins.extend(self._os_tpls(req)) mixins.extend(self._os_tpls(req))
# OpenStack Contextualization # OpenStack Contextualization
mixins.append(os_contextualization.user_data)
mixins.append(os_contextualization.public_key)
# OCCI Contextualization
mixins.append(contextualization.user_data) mixins.append(contextualization.user_data)
mixins.append(contextualization.public_key) mixins.append(contextualization.ssh_key)
# OpenStack Floating IP Pools # OpenStack Floating IP Pools
mixins.extend(self._ip_pools(req)) mixins.extend(self._ip_pools(req))

View File

@ -147,3 +147,8 @@ class NetworkPoolFound(NotFound):
class MissingKeypairName(Invalid): class MissingKeypairName(Invalid):
msg_fmt = "Missing Keypair Name" msg_fmt = "Missing Keypair Name"
code = 400 code = 400
class OCCIMixinConflict(OCCIException):
msg_fmt = "Conflicting OCCI Mixins used in request"
code = 409

View File

@ -0,0 +1,85 @@
# Copyright 2015 Spanish National Research Council
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from ooi.occi.core import attribute
from ooi.occi.core import mixin
from ooi.occi import helpers
from ooi.occi.infrastructure import compute
class SSHKey(mixin.Mixin):
scheme = helpers.build_scheme("infrastructure/credentials")
term = "ssh_key"
def __init__(self, ssh_key=None):
attrs = [
attribute.MutableAttribute(
"occi.crendentials.ssh.publickey", ssh_key, required=True,
description=("The contents of the public key file to be "
"injected into the Compute Resource"),
attr_type=attribute.AttributeType.string_type),
]
attrs = attribute.AttributeCollection({a.name: a for a in attrs})
super(SSHKey, self).__init__(SSHKey.scheme, SSHKey.term,
"Credentials mixin",
attributes=attrs,
applies=[compute.ComputeResource.kind])
@property
def ssh_key(self):
attr = "occi.crendentials.ssh.publickey"
return self.attributes[attr].value
@ssh_key.setter
def ssh_key(self, value):
attr = "occi.crendentials.ssh.publickey"
self.attributes[attr].value = value
class UserData(mixin.Mixin):
scheme = helpers.build_scheme("infrastructure/compute")
term = "user_data"
def __init__(self, user_data=None):
attrs = [
attribute.MutableAttribute(
"occi.compute.userdata", user_data, required=True,
description=("Contextualization data (e.g., script, "
"executable) that the client supplies once and "
"only once. It cannot be updated."),
attr_type=attribute.AttributeType.string_type),
]
attrs = attribute.AttributeCollection({a.name: a for a in attrs})
super(UserData, self).__init__(UserData.scheme,
UserData.term,
"Contextualization mixin",
attributes=attrs,
applies=[compute.ComputeResource.kind])
@property
def user_data(self):
attr = "occi.compute.userdata"
return self.attributes[attr].value
@user_data.setter
def user_data(self, value):
attr = "occi.compute.userdata"
self.attributes[attr].value = value
ssh_key = SSHKey()
user_data = UserData()

View File

@ -409,6 +409,16 @@ def fake_query_results():
'scheme="http://schemas.openstack.org/instance/credentials#"; ' 'scheme="http://schemas.openstack.org/instance/credentials#"; '
'class="mixin"; title="Contextualization extension - public_key"') 'class="mixin"; title="Contextualization extension - public_key"')
# OCCI contextualization
cats.append(
'user_data; '
'scheme="http://schemas.ogf.org/occi/infrastructure/compute#"; '
'class="mixin"; title="Contextualization mixin"')
cats.append(
'ssh_key; '
'scheme="http://schemas.ogf.org/occi/infrastructure/credentials#"; '
'class="mixin"; title="Credentials mixin"')
result = [] result = []
for c in cats: for c in cats:
result.append(("Category", c)) result.append(("Category", c))

View File

@ -257,7 +257,7 @@ class TestComputeController(test_middleware.TestMiddleware):
self.assertEqual(400, resp.status_code) self.assertEqual(400, resp.status_code)
self.assertDefaults(resp) self.assertDefaults(resp)
def test_create_with_context(self): def test_create_with_os_context(self):
tenant = fakes.tenants["foo"] tenant = fakes.tenants["foo"]
app = self.get_app() app = self.get_app()
@ -292,6 +292,41 @@ class TestComputeController(test_middleware.TestMiddleware):
self.assertExpectedResult(expected, resp) self.assertExpectedResult(expected, resp)
self.assertDefaults(resp) self.assertDefaults(resp)
def test_create_with_occi_context(self):
tenant = fakes.tenants["foo"]
app = self.get_app()
headers = {
'Category': (
'compute;'
'scheme="http://schemas.ogf.org/occi/infrastructure#";'
'class="kind",'
'foo;'
'scheme="http://schemas.openstack.org/template/resource#";'
'class="mixin",'
'bar;'
'scheme="http://schemas.openstack.org/template/os#";'
'class="mixin",'
'user_data;'
'scheme="http://schemas.ogf.org/occi/infrastructure/compute#";'
'class="mixin"'
),
'X-OCCI-Attribute': (
'occi.compute.user_data="foo"'
)
}
req = self._build_req("/compute", tenant["id"], method="POST",
headers=headers)
resp = req.get_response(app)
expected = [("X-OCCI-Location",
utils.join_url(self.application_url + "/",
"compute/%s" % "foo"))]
self.assertEqual(200, resp.status_code)
self.assertExpectedResult(expected, resp)
self.assertDefaults(resp)
def test_create_vm_with_storage(self): def test_create_vm_with_storage(self):
tenant = fakes.tenants["foo"] tenant = fakes.tenants["foo"]
target = utils.join_url(self.application_url + "/", target = utils.join_url(self.application_url + "/",

View File

@ -24,9 +24,10 @@ from ooi.api import helpers
from ooi import exception from ooi import exception
from ooi.occi.core import collection from ooi.occi.core import collection
from ooi.occi.infrastructure import compute as occi_compute from ooi.occi.infrastructure import compute as occi_compute
from ooi.occi.infrastructure import contextualization
from ooi.occi.infrastructure import network as occi_network from ooi.occi.infrastructure import network as occi_network
from ooi.occi.infrastructure import storage as occi_storage from ooi.occi.infrastructure import storage as occi_storage
from ooi.openstack import contextualization from ooi.openstack import contextualization as os_contextualization
from ooi.openstack import templates from ooi.openstack import templates
from ooi.tests import base from ooi.tests import base
from ooi.tests import fakes from ooi.tests import fakes
@ -272,8 +273,8 @@ class TestComputeController(base.TestController):
@mock.patch.object(helpers.OpenStackHelper, "create_server") @mock.patch.object(helpers.OpenStackHelper, "create_server")
@mock.patch.object(compute.Controller, "_get_network_from_req") @mock.patch.object(compute.Controller, "_get_network_from_req")
@mock.patch("ooi.occi.validator.Validator") @mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_context(self, m_validator, m_net, def test_create_server_with_os_context(self, m_validator, m_net,
m_create): m_create):
tenant = fakes.tenants["foo"] tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"]) req = self._build_req(tenant["id"])
obj = { obj = {
@ -284,8 +285,7 @@ class TestComputeController(base.TestController):
"schemes": { "schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"], templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"], templates.OpenStackResourceTemplate.scheme: ["bar"],
contextualization.user_data.scheme: None, os_contextualization.user_data.scheme: None,
contextualization.public_key.scheme: None,
}, },
} }
# NOTE(aloga): the mocked call is # NOTE(aloga): the mocked call is
@ -306,12 +306,69 @@ class TestComputeController(base.TestController):
block_device_mapping_v2=[], block_device_mapping_v2=[],
networks=net) networks=net)
@mock.patch.object(helpers.OpenStackHelper, "create_server")
@mock.patch.object(compute.Controller, "_get_network_from_req")
@mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_occi_context(self, m_validator, m_net,
m_create):
tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"])
obj = {
"attributes": {
"occi.core.title": "foo instance",
"occi.compute.user_data": "bazonk",
},
"schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"],
contextualization.user_data.scheme: None,
},
}
# NOTE(aloga): the mocked call is
# "parser = req.get_parser()(req.headers, req.body)"
req.get_parser = mock.MagicMock()
# NOTE(aloga): MOG!
req.get_parser.return_value.return_value.parse.return_value = obj
m_validator.validate.return_value = True
server = {"id": uuid.uuid4().hex}
m_create.return_value = server
net = [{'uuid': uuid.uuid4().hex}]
m_net.return_value = net
ret = self.controller.create(req, None) # noqa
self.assertIsInstance(ret, collection.Collection)
m_create.assert_called_with(mock.ANY, "foo instance", "foo", "bar",
user_data="bazonk",
key_name=None,
block_device_mapping_v2=[],
networks=net)
@mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_context_conflict(self, m_validator):
tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"])
obj = {
"attributes": {
"occi.core.title": "foo instance",
},
"schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"],
os_contextualization.user_data.scheme: None,
contextualization.user_data.scheme: None,
},
}
req.get_parser = mock.MagicMock()
req.get_parser.return_value.return_value.parse.return_value = obj
m_validator.validate.return_value = True
self.assertRaises(exception.OCCIMixinConflict, self.controller.create,
req, None)
@mock.patch.object(helpers.OpenStackHelper, "keypair_create") @mock.patch.object(helpers.OpenStackHelper, "keypair_create")
@mock.patch.object(helpers.OpenStackHelper, "create_server") @mock.patch.object(helpers.OpenStackHelper, "create_server")
@mock.patch.object(compute.Controller, "_get_network_from_req") @mock.patch.object(compute.Controller, "_get_network_from_req")
@mock.patch("ooi.occi.validator.Validator") @mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_sshkeys(self, m_validator, m_net, def test_create_server_with_os_sshkey(self, m_validator, m_net,
m_server, m_keypair): m_server, m_keypair):
tenant = fakes.tenants["foo"] tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"]) req = self._build_req(tenant["id"])
obj = { obj = {
@ -323,7 +380,7 @@ class TestComputeController(base.TestController):
"schemes": { "schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"], templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"], templates.OpenStackResourceTemplate.scheme: ["bar"],
contextualization.public_key.scheme: None, os_contextualization.public_key.scheme: None,
}, },
} }
req.get_parser = mock.MagicMock() req.get_parser = mock.MagicMock()
@ -349,9 +406,9 @@ class TestComputeController(base.TestController):
@mock.patch.object(helpers.OpenStackHelper, "create_server") @mock.patch.object(helpers.OpenStackHelper, "create_server")
@mock.patch.object(compute.Controller, "_get_network_from_req") @mock.patch.object(compute.Controller, "_get_network_from_req")
@mock.patch("ooi.occi.validator.Validator") @mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_no_sshkey_name(self, m_validator, m_net, def test_create_server_with_os_sshkey_no_name(self, m_validator, m_net,
m_server, m_keypair, m_server, m_keypair,
m_keypair_delete): m_keypair_delete):
tenant = fakes.tenants["foo"] tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"]) req = self._build_req(tenant["id"])
obj = { obj = {
@ -362,7 +419,7 @@ class TestComputeController(base.TestController):
"schemes": { "schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"], templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"], templates.OpenStackResourceTemplate.scheme: ["bar"],
contextualization.public_key.scheme: None, os_contextualization.public_key.scheme: None,
}, },
} }
req.get_parser = mock.MagicMock() req.get_parser = mock.MagicMock()
@ -384,6 +441,67 @@ class TestComputeController(base.TestController):
networks=net) networks=net)
m_keypair_delete.assert_called_with(mock.ANY, mock.ANY) m_keypair_delete.assert_called_with(mock.ANY, mock.ANY)
@mock.patch.object(helpers.OpenStackHelper, "keypair_delete")
@mock.patch.object(helpers.OpenStackHelper, "keypair_create")
@mock.patch.object(helpers.OpenStackHelper, "create_server")
@mock.patch.object(compute.Controller, "_get_network_from_req")
@mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_occi_sshkey(self, m_validator, m_net,
m_server, m_keypair,
m_keypair_delete):
tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"])
obj = {
"attributes": {
"occi.core.title": "foo instance",
"occi.credentials.ssh_key": "wtfoodata"
},
"schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"],
contextualization.ssh_key.scheme: None,
},
}
req.get_parser = mock.MagicMock()
req.get_parser.return_value.return_value.parse.return_value = obj
m_validator.validate.return_value = True
server = {"id": uuid.uuid4().hex}
m_server.return_value = server
m_keypair.return_value = None
net = [{'uuid': uuid.uuid4().hex}]
m_net.return_value = net
ret = self.controller.create(req, None) # noqa
self.assertIsInstance(ret, collection.Collection)
m_keypair.assert_called_with(mock.ANY, mock.ANY,
public_key="wtfoodata")
m_server.assert_called_with(mock.ANY, "foo instance", "foo", "bar",
user_data=None,
key_name=mock.ANY,
block_device_mapping_v2=[],
networks=net)
m_keypair_delete.assert_called_with(mock.ANY, mock.ANY)
@mock.patch("ooi.occi.validator.Validator")
def test_create_server_with_sshkey_conflict(self, m_validator):
tenant = fakes.tenants["foo"]
req = self._build_req(tenant["id"])
obj = {
"attributes": {
"occi.core.title": "foo instance",
},
"schemes": {
templates.OpenStackOSTemplate.scheme: ["foo"],
templates.OpenStackResourceTemplate.scheme: ["bar"],
os_contextualization.public_key.scheme: None,
contextualization.ssh_key.scheme: None,
},
}
req.get_parser = mock.MagicMock()
req.get_parser.return_value.return_value.parse.return_value = obj
m_validator.validate.return_value = True
self.assertRaises(exception.OCCIMixinConflict, self.controller.create,
req, None)
def test_build_block_mapping_no_links(self): def test_build_block_mapping_no_links(self):
ret = self.controller._build_block_mapping(None, {}) ret = self.controller._build_block_mapping(None, {})
self.assertEqual([], ret) self.assertEqual([], ret)

View File

@ -20,12 +20,13 @@ from ooi.occi.core import entity
from ooi.occi.core import link from ooi.occi.core import link
from ooi.occi.core import resource from ooi.occi.core import resource
from ooi.occi.infrastructure import compute from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import contextualization
from ooi.occi.infrastructure import network from ooi.occi.infrastructure import network
from ooi.occi.infrastructure import network_link from ooi.occi.infrastructure import network_link
from ooi.occi.infrastructure import storage from ooi.occi.infrastructure import storage
from ooi.occi.infrastructure import storage_link from ooi.occi.infrastructure import storage_link
from ooi.occi.infrastructure import templates as infra_templates from ooi.occi.infrastructure import templates as infra_templates
from ooi.openstack import contextualization from ooi.openstack import contextualization as os_contextualization
from ooi.openstack import network as os_network from ooi.openstack import network as os_network
from ooi.openstack import templates from ooi.openstack import templates
from ooi.tests import base from ooi.tests import base
@ -74,8 +75,10 @@ class TestQueryController(base.TestController):
network_link.ip_network_interface, network_link.ip_network_interface,
infra_templates.os_tpl, infra_templates.os_tpl,
infra_templates.resource_tpl, infra_templates.resource_tpl,
os_contextualization.user_data,
os_contextualization.public_key,
contextualization.user_data, contextualization.user_data,
contextualization.public_key, contextualization.ssh_key,
] ]
expected_actions = [ expected_actions = [
@ -141,8 +144,10 @@ class TestQueryController(base.TestController):
network_link.ip_network_interface, network_link.ip_network_interface,
infra_templates.os_tpl, infra_templates.os_tpl,
infra_templates.resource_tpl, infra_templates.resource_tpl,
os_contextualization.user_data,
os_contextualization.public_key,
contextualization.user_data, contextualization.user_data,
contextualization.public_key, contextualization.ssh_key,
] ]
expected_actions = [ expected_actions = [

View File

@ -18,6 +18,7 @@ from ooi.occi.core import link
from ooi.occi.core import mixin from ooi.occi.core import mixin
from ooi.occi.core import resource from ooi.occi.core import resource
from ooi.occi.infrastructure import compute from ooi.occi.infrastructure import compute
from ooi.occi.infrastructure import contextualization
from ooi.occi.infrastructure import network from ooi.occi.infrastructure import network
from ooi.occi.infrastructure import network_link from ooi.occi.infrastructure import network_link
from ooi.occi.infrastructure import storage from ooi.occi.infrastructure import storage
@ -340,3 +341,21 @@ class TestOCCINetworkInterface(base.TestCase):
self.assertEqual("00:01:02:03:04:05", l.mac) self.assertEqual("00:01:02:03:04:05", l.mac)
self.assertEqual("foo", l.state) self.assertEqual("foo", l.state)
self.assertEqual("msg", l.message) self.assertEqual("msg", l.message)
class TestOCCIUserData(base.TestCase):
def test_occi_userdata(self):
user_data = "foobar"
mxn = contextualization.UserData(user_data)
self.assertEqual("user_data", mxn.term)
self.assertEqual(user_data, mxn.user_data)
self.assertEqual([compute.ComputeResource.kind], mxn.applies)
class TestOCCISSHKey(base.TestCase):
def test_occi_ssh_key(self):
key_data = "1234"
mxn = contextualization.SSHKey(key_data)
self.assertEqual("ssh_key", mxn.term)
self.assertEqual(key_data, mxn.ssh_key)
self.assertEqual([compute.ComputeResource.kind], mxn.applies)