Add support for SSH key injection.
Change-Id: I64274080402ecc570dd1561a9ee5afdbb7970264
This commit is contained in:
parent
02fbf47a49
commit
7ad885011e
@ -114,12 +114,24 @@ class Controller(ooi.api.base.Controller):
|
||||
name = attrs.get("occi.core.title", "OCCI VM")
|
||||
image = obj["schemes"][templates.OpenStackOSTemplate.scheme][0]
|
||||
flavor = obj["schemes"][templates.OpenStackResourceTemplate.scheme][0]
|
||||
user_data = None
|
||||
user_data, key_name, key_data = None, None, None
|
||||
if contextualization.user_data.scheme in obj["schemes"]:
|
||||
user_data = attrs.get("org.openstack.compute.user_data")
|
||||
if contextualization.public_key.scheme in obj["schemes"]:
|
||||
key_name = attrs.get("org.openstack.credentials.publickey.name")
|
||||
key_data = attrs.get("org.openstack.credentials.publickey.data")
|
||||
|
||||
if key_name and key_data:
|
||||
# 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)
|
||||
elif not key_name and key_data:
|
||||
raise exception.MissingKeypairName
|
||||
|
||||
server = self.os_helper.create_server(req, name, image, flavor,
|
||||
user_data=user_data)
|
||||
user_data=user_data,
|
||||
key_name=key_name)
|
||||
# The returned JSON does not contain the server name
|
||||
server["name"] = name
|
||||
occi_compute_resources = self._get_compute_resources([server])
|
||||
|
@ -201,7 +201,9 @@ class OpenStackHelper(BaseHelper):
|
||||
response = req.get_response(self.app)
|
||||
return self.get_from_response(response, "server", {})
|
||||
|
||||
def _get_create_server_req(self, req, name, image, flavor, user_data=None):
|
||||
def _get_create_server_req(self, req, name, image, flavor,
|
||||
user_data=None,
|
||||
key_name=None):
|
||||
tenant_id = self.tenant_from_req(req)
|
||||
path = "/%s/servers" % tenant_id
|
||||
# TODO(enolfc): add here the correct metadata info
|
||||
@ -212,15 +214,20 @@ class OpenStackHelper(BaseHelper):
|
||||
"imageRef": image,
|
||||
"flavorRef": flavor,
|
||||
}}
|
||||
|
||||
if user_data is not None:
|
||||
body["server"]["user_data"] = user_data
|
||||
if key_name is not None:
|
||||
body["server"]["key_name"] = key_name
|
||||
|
||||
return self._get_req(req,
|
||||
path=path,
|
||||
content_type="application/json",
|
||||
body=json.dumps(body),
|
||||
method="POST")
|
||||
|
||||
def create_server(self, req, name, image, flavor, user_data=None):
|
||||
def create_server(self, req, name, image, flavor,
|
||||
user_data=None, key_name=None):
|
||||
"""Create a server.
|
||||
|
||||
:param req: the incoming request
|
||||
@ -228,9 +235,11 @@ class OpenStackHelper(BaseHelper):
|
||||
:param image: image id for the new server
|
||||
:param flavor: flavor id for the new server
|
||||
:param user_data: user data to inject into the server
|
||||
:param key_name: user public key name
|
||||
"""
|
||||
req = self._get_create_server_req(req, name, image, flavor,
|
||||
user_data=user_data)
|
||||
user_data=user_data,
|
||||
key_name=key_name)
|
||||
response = req.get_response(self.app)
|
||||
# We only get one server
|
||||
return self.get_from_response(response, "server", {})
|
||||
@ -509,3 +518,29 @@ class OpenStackHelper(BaseHelper):
|
||||
response = req.get_response(self.app)
|
||||
if response.status_int != 202:
|
||||
raise exception_from_response(response)
|
||||
|
||||
def _get_keypair_create_req(self, req, name, public_key=None):
|
||||
tenant_id = self.tenant_from_req(req)
|
||||
path = "/%s/os-keypairs" % tenant_id
|
||||
body = {"keypair": {
|
||||
"name": name,
|
||||
}}
|
||||
if public_key:
|
||||
body["keypair"]["public_key"] = public_key
|
||||
|
||||
return self._get_req(req,
|
||||
path=path,
|
||||
content_type="application/json",
|
||||
body=json.dumps(body),
|
||||
method="POST")
|
||||
|
||||
def keypair_create(self, req, name, public_key=None):
|
||||
"""Create a keypair.
|
||||
|
||||
:param req: the incoming request
|
||||
:param name: name for the new keypair
|
||||
:param public_key: public ssh key to import
|
||||
"""
|
||||
req = self._get_keypair_create_req(req, name, public_key=public_key)
|
||||
response = req.get_response(self.app)
|
||||
return self.get_from_response(response, "keypair", {})
|
||||
|
@ -130,3 +130,8 @@ class NetworkNotFound(NotFound):
|
||||
|
||||
class NetworkPoolFound(NotFound):
|
||||
msg_fmt = "Network Pool Not Found: '%(pool)s'"
|
||||
|
||||
|
||||
class MissingKeypairName(Invalid):
|
||||
msg_fmt = "Missing Keypair Name"
|
||||
code = 400
|
||||
|
@ -183,7 +183,8 @@ class TestComputeController(base.TestController):
|
||||
ret = self.controller.create(req, None)
|
||||
self.assertIsInstance(ret, collection.Collection)
|
||||
m_create.assert_called_with(mock.ANY, "foo instance", "foo", "bar",
|
||||
user_data=None)
|
||||
user_data=None,
|
||||
key_name=None)
|
||||
|
||||
@mock.patch.object(helpers.OpenStackHelper, "create_server")
|
||||
@mock.patch("ooi.occi.validator.Validator")
|
||||
@ -193,12 +194,13 @@ class TestComputeController(base.TestController):
|
||||
obj = {
|
||||
"attributes": {
|
||||
"occi.core.title": "foo instance",
|
||||
"org.openstack.compute.user_data": "bazonk"
|
||||
"org.openstack.compute.user_data": "bazonk",
|
||||
},
|
||||
"schemes": {
|
||||
templates.OpenStackOSTemplate.scheme: ["foo"],
|
||||
templates.OpenStackResourceTemplate.scheme: ["bar"],
|
||||
contextualization.user_data.scheme: None,
|
||||
contextualization.public_key.scheme: None,
|
||||
},
|
||||
}
|
||||
# NOTE(aloga): the mocked call is
|
||||
@ -212,4 +214,62 @@ class TestComputeController(base.TestController):
|
||||
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")
|
||||
user_data="bazonk",
|
||||
key_name=None)
|
||||
|
||||
@mock.patch("ooi.occi.validator.Validator")
|
||||
def test_create_server_with_sshkeys_invalid(self, m_validator):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
obj = {
|
||||
"attributes": {
|
||||
"occi.core.title": "foo instance",
|
||||
"org.openstack.credentials.publickey.name": "",
|
||||
"org.openstack.credentials.publickey.data": "wtfoodata"
|
||||
},
|
||||
"schemes": {
|
||||
templates.OpenStackOSTemplate.scheme: ["foo"],
|
||||
templates.OpenStackResourceTemplate.scheme: ["bar"],
|
||||
contextualization.public_key.scheme: None,
|
||||
},
|
||||
}
|
||||
|
||||
req.get_parser = mock.MagicMock()
|
||||
req.get_parser.return_value.return_value.parse.return_value = obj
|
||||
self.assertRaises(exception.MissingKeypairName,
|
||||
self.controller.create,
|
||||
req,
|
||||
None)
|
||||
|
||||
@mock.patch.object(helpers.OpenStackHelper, "keypair_create")
|
||||
@mock.patch.object(helpers.OpenStackHelper, "create_server")
|
||||
@mock.patch("ooi.occi.validator.Validator")
|
||||
def test_create_server_with_sshkeys(self, m_validator, m_server,
|
||||
m_keypair):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
obj = {
|
||||
"attributes": {
|
||||
"occi.core.title": "foo instance",
|
||||
"org.openstack.credentials.publickey.name": "wtfoo",
|
||||
"org.openstack.credentials.publickey.data": "wtfoodata"
|
||||
},
|
||||
"schemes": {
|
||||
templates.OpenStackOSTemplate.scheme: ["foo"],
|
||||
templates.OpenStackResourceTemplate.scheme: ["bar"],
|
||||
contextualization.public_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
|
||||
ret = self.controller.create(req, None) # noqa
|
||||
self.assertIsInstance(ret, collection.Collection)
|
||||
m_keypair.assert_called_with(mock.ANY, "wtfoo",
|
||||
public_key="wtfoodata")
|
||||
m_server.assert_called_with(mock.ANY, "foo instance", "foo", "bar",
|
||||
user_data=None,
|
||||
key_name="wtfoo")
|
||||
|
@ -554,10 +554,13 @@ class TestOpenStackHelper(TestBaseHelper):
|
||||
image = uuid.uuid4().hex
|
||||
flavor = uuid.uuid4().hex
|
||||
user_data = "foo"
|
||||
key_name = "wtfoo"
|
||||
ret = self.helper.create_server(None, name, image, flavor,
|
||||
user_data=user_data)
|
||||
user_data=user_data,
|
||||
key_name=key_name)
|
||||
self.assertEqual("FOO", ret)
|
||||
m.assert_called_with(None, name, image, flavor, user_data=user_data)
|
||||
m.assert_called_with(None, name, image, flavor, user_data=user_data,
|
||||
key_name=key_name)
|
||||
|
||||
@mock.patch("ooi.api.helpers.exception_from_response")
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_create_server_req")
|
||||
@ -571,6 +574,7 @@ class TestOpenStackHelper(TestBaseHelper):
|
||||
image = uuid.uuid4().hex
|
||||
flavor = uuid.uuid4().hex
|
||||
user_data = "foo"
|
||||
key_name = "wtfoo"
|
||||
m_exc.return_value = webob.exc.HTTPInternalServerError()
|
||||
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||
self.helper.create_server,
|
||||
@ -578,8 +582,10 @@ class TestOpenStackHelper(TestBaseHelper):
|
||||
name,
|
||||
image,
|
||||
flavor,
|
||||
user_data=user_data)
|
||||
m.assert_called_with(None, name, image, flavor, user_data=user_data)
|
||||
user_data=user_data,
|
||||
key_name=key_name)
|
||||
m.assert_called_with(None, name, image, flavor, user_data=user_data,
|
||||
key_name=key_name)
|
||||
m_exc.assert_called_with(resp)
|
||||
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_volume_create_req")
|
||||
@ -968,6 +974,28 @@ class TestOpenStackHelperReqs(TestBaseHelper):
|
||||
user_data=user_data)
|
||||
self.assertExpectedReq("POST", path, body, os_req)
|
||||
|
||||
def test_get_os_get_server_create_with_key_name(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
name = "foo server"
|
||||
image = "bar image"
|
||||
flavor = "baz flavor"
|
||||
key_name = "wtfoo"
|
||||
|
||||
body = {
|
||||
"server": {
|
||||
"name": name,
|
||||
"imageRef": image,
|
||||
"flavorRef": flavor,
|
||||
"key_name": key_name,
|
||||
},
|
||||
}
|
||||
|
||||
path = "/%s/servers" % tenant["id"]
|
||||
os_req = self.helper._get_create_server_req(req, name, image, flavor,
|
||||
key_name=key_name)
|
||||
self.assertExpectedReq("POST", path, body, os_req)
|
||||
|
||||
def test_get_os_get_volume_create(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
@ -1021,3 +1049,97 @@ class TestOpenStackHelperReqs(TestBaseHelper):
|
||||
path = "/%s/servers/%s/action" % (tenant["id"], server)
|
||||
os_req = self.helper._get_remove_floating_ip_req(req, server, ip)
|
||||
self.assertExpectedReq("POST", path, body, os_req)
|
||||
|
||||
def test_get_os_get_keypair_create(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
name = "fookey"
|
||||
|
||||
body = {
|
||||
"keypair": {
|
||||
"name": name,
|
||||
}
|
||||
}
|
||||
|
||||
path = "/%s/os-keypairs" % tenant["id"]
|
||||
os_req = self.helper._get_keypair_create_req(req, name)
|
||||
self.assertExpectedReq("POST", path, body, os_req)
|
||||
|
||||
def test_get_os_get_keypair_create_import(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
req = self._build_req(tenant["id"])
|
||||
name = "fookey"
|
||||
public_key = "fookeydata"
|
||||
|
||||
body = {
|
||||
"keypair": {
|
||||
"name": name,
|
||||
"public_key": public_key
|
||||
}
|
||||
}
|
||||
|
||||
path = "/%s/os-keypairs" % tenant["id"]
|
||||
os_req = self.helper._get_keypair_create_req(req, name,
|
||||
public_key=public_key)
|
||||
self.assertExpectedReq("POST", path, body, os_req)
|
||||
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_keypair_create_req")
|
||||
def test_keypair_create(self, m):
|
||||
resp = fakes.create_fake_json_resp({"keypair": "FOO"}, 200)
|
||||
req_mock = mock.MagicMock()
|
||||
req_mock.get_response.return_value = resp
|
||||
m.return_value = req_mock
|
||||
name = uuid.uuid4().hex
|
||||
public_key = None
|
||||
ret = self.helper.keypair_create(None, name)
|
||||
self.assertEqual("FOO", ret)
|
||||
m.assert_called_with(None, name, public_key=public_key)
|
||||
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_keypair_create_req")
|
||||
def test_keypair_create_key_import(self, m):
|
||||
resp = fakes.create_fake_json_resp({"keypair": "FOO"}, 200)
|
||||
req_mock = mock.MagicMock()
|
||||
req_mock.get_response.return_value = resp
|
||||
m.return_value = req_mock
|
||||
name = uuid.uuid4().hex
|
||||
public_key = "fookeydata"
|
||||
ret = self.helper.keypair_create(None, name, public_key=public_key)
|
||||
self.assertEqual("FOO", ret)
|
||||
m.assert_called_with(None, name, public_key=public_key)
|
||||
|
||||
@mock.patch("ooi.api.helpers.exception_from_response")
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_keypair_create_req")
|
||||
def test_keypair_create_with_exception(self, m, m_exc):
|
||||
fault = {"computeFault": {"message": "bad", "code": 500}}
|
||||
resp = fakes.create_fake_json_resp(fault, 500)
|
||||
req_mock = mock.MagicMock()
|
||||
req_mock.get_response.return_value = resp
|
||||
m.return_value = req_mock
|
||||
name = uuid.uuid4().hex
|
||||
m_exc.return_value = webob.exc.HTTPInternalServerError()
|
||||
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||
self.helper.keypair_create,
|
||||
None,
|
||||
name,
|
||||
None)
|
||||
m.assert_called_with(None, name, public_key=None)
|
||||
m_exc.assert_called_with(resp)
|
||||
|
||||
@mock.patch("ooi.api.helpers.exception_from_response")
|
||||
@mock.patch.object(helpers.OpenStackHelper, "_get_keypair_create_req")
|
||||
def test_keypair_create_key_import_with_exception(self, m, m_exc):
|
||||
fault = {"computeFault": {"message": "bad", "code": 500}}
|
||||
resp = fakes.create_fake_json_resp(fault, 500)
|
||||
req_mock = mock.MagicMock()
|
||||
req_mock.get_response.return_value = resp
|
||||
m.return_value = req_mock
|
||||
name = uuid.uuid4().hex
|
||||
public_key = "fookeydata"
|
||||
m_exc.return_value = webob.exc.HTTPInternalServerError()
|
||||
self.assertRaises(webob.exc.HTTPInternalServerError,
|
||||
self.helper.keypair_create,
|
||||
None,
|
||||
name,
|
||||
public_key)
|
||||
m.assert_called_with(None, name, public_key=public_key)
|
||||
m_exc.assert_called_with(resp)
|
||||
|
Loading…
Reference in New Issue
Block a user