Use Resource layer for next compute methods
- server_groups - server_console - create_image - server_metadata Change-Id: I26f3a22bf9d9e397c2a032cf2b66ec60d8040b51
This commit is contained in:
parent
f7860861a1
commit
9cce631094
|
@ -363,7 +363,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
# and the to_munch call.
|
||||
self._normalize_server(server._to_munch())
|
||||
for server in self.compute.servers(
|
||||
all_projects=all_projects, **filters)]
|
||||
all_projects=all_projects, allow_unknown_params=True,
|
||||
**filters)]
|
||||
return [
|
||||
self._expand_server(server, detailed, bare)
|
||||
for server in servers
|
||||
|
@ -375,10 +376,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
:returns: A list of server group dicts.
|
||||
|
||||
"""
|
||||
data = proxy._json_response(
|
||||
self.compute.get('/os-server-groups'),
|
||||
error_message="Error fetching server group list")
|
||||
return self._get_and_munchify('server_groups', data)
|
||||
return list(self.compute.server_groups())
|
||||
|
||||
def get_compute_limits(self, name_or_id=None):
|
||||
""" Get compute limits for a project
|
||||
|
@ -521,10 +519,12 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
return ""
|
||||
|
||||
def _get_server_console_output(self, server_id, length=None):
|
||||
data = proxy._json_response(self.compute.post(
|
||||
'/servers/{server_id}/action'.format(server_id=server_id),
|
||||
json={'os-getConsoleOutput': {'length': length}}))
|
||||
return self._get_and_munchify('output', data)
|
||||
output = self.compute.get_server_console_output(
|
||||
server=server_id,
|
||||
length=length
|
||||
)
|
||||
if 'output' in output:
|
||||
return output['output']
|
||||
|
||||
def get_server(
|
||||
self, name_or_id=None, filters=None, detailed=False, bare=False,
|
||||
|
@ -678,40 +678,9 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
"Server {server} could not be found and therefore"
|
||||
" could not be snapshotted.".format(server=server))
|
||||
server = server_obj
|
||||
response = proxy._json_response(
|
||||
self.compute.post(
|
||||
'/servers/{server_id}/action'.format(server_id=server['id']),
|
||||
json={
|
||||
"createImage": {
|
||||
"name": name,
|
||||
"metadata": metadata,
|
||||
}
|
||||
}))
|
||||
# You won't believe it - wait, who am I kidding - of course you will!
|
||||
# Nova returns the URL of the image created in the Location
|
||||
# header of the response. (what?) But, even better, the URL it responds
|
||||
# with has a very good chance of being wrong (it is built from
|
||||
# nova.conf values that point to internal API servers in any cloud
|
||||
# large enough to have both public and internal endpoints.
|
||||
# However, nobody has ever noticed this because novaclient doesn't
|
||||
# actually use that URL - it extracts the id from the end of
|
||||
# the url, then returns the id. This leads us to question:
|
||||
# a) why Nova is going to return a value in a header
|
||||
# b) why it's going to return data that probably broken
|
||||
# c) indeed the very nature of the fabric of reality
|
||||
# Although it fills us with existential dread, we have no choice but
|
||||
# to follow suit like a lemming being forced over a cliff by evil
|
||||
# producers from Disney.
|
||||
# TODO(mordred) Update this to consume json microversion when it is
|
||||
# available.
|
||||
# blueprint:remove-create-image-location-header-response
|
||||
image_id = response.headers['Location'].rsplit('/', 1)[1]
|
||||
self.list_images.invalidate(self)
|
||||
image = self.get_image(image_id)
|
||||
|
||||
if not wait:
|
||||
return image
|
||||
return self.wait_for_image(image, timeout=timeout)
|
||||
image = self.compute.create_server_image(
|
||||
server, name=name, metadata=metadata, wait=wait, timeout=timeout)
|
||||
return image
|
||||
|
||||
def get_server_id(self, name_or_id):
|
||||
server = self.get_server(name_or_id, bare=True)
|
||||
|
@ -1100,6 +1069,9 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
"""
|
||||
Wait for a server to reach ACTIVE status.
|
||||
"""
|
||||
# server = self.compute.wait_for_server(
|
||||
# server=server, interval=self._SERVER_AGE or 2, wait=timeout
|
||||
# )
|
||||
server_id = server['id']
|
||||
timeout_message = "Timeout waiting for the server to come up."
|
||||
start_time = time.time()
|
||||
|
@ -1238,11 +1210,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
raise exc.OpenStackCloudException(
|
||||
'Invalid Server {server}'.format(server=name_or_id))
|
||||
|
||||
proxy._json_response(
|
||||
self.compute.post(
|
||||
'/servers/{server_id}/metadata'.format(server_id=server['id']),
|
||||
json={'metadata': metadata}),
|
||||
error_message='Error updating server metadata')
|
||||
self.compute.set_server_metadata(server=server.id, **metadata)
|
||||
|
||||
def delete_server_metadata(self, name_or_id, metadata_keys):
|
||||
"""Delete metadata from a server instance.
|
||||
|
@ -1259,15 +1227,8 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
raise exc.OpenStackCloudException(
|
||||
'Invalid Server {server}'.format(server=name_or_id))
|
||||
|
||||
for key in metadata_keys:
|
||||
error_message = 'Error deleting metadata {key} on {server}'.format(
|
||||
key=key, server=name_or_id)
|
||||
proxy._json_response(
|
||||
self.compute.delete(
|
||||
'/servers/{server_id}/metadata/{key}'.format(
|
||||
server_id=server['id'],
|
||||
key=key)),
|
||||
error_message=error_message)
|
||||
self.compute.delete_server_metadata(server=server.id,
|
||||
keys=metadata_keys)
|
||||
|
||||
def delete_server(
|
||||
self, name_or_id, wait=False, timeout=180, delete_ips=False,
|
||||
|
@ -1410,7 +1371,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
self._get_and_munchify('server', data))
|
||||
return self._expand_server(server, bare=bare, detailed=detailed)
|
||||
|
||||
def create_server_group(self, name, policies):
|
||||
def create_server_group(self, name, policies=[], policy=None):
|
||||
"""Create a new server group.
|
||||
|
||||
:param name: Name of the server group being created
|
||||
|
@ -1420,16 +1381,16 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
|
||||
:raises: OpenStackCloudException on operation error.
|
||||
"""
|
||||
data = proxy._json_response(
|
||||
self.compute.post(
|
||||
'/os-server-groups',
|
||||
json={
|
||||
'server_group': {
|
||||
'name': name,
|
||||
'policies': policies}}),
|
||||
error_message="Unable to create server group {name}".format(
|
||||
name=name))
|
||||
return self._get_and_munchify('server_group', data)
|
||||
sg_attrs = {
|
||||
'name': name
|
||||
}
|
||||
if policies:
|
||||
sg_attrs['policies'] = policies
|
||||
if policy:
|
||||
sg_attrs['policy'] = policy
|
||||
return self.compute.create_server_group(
|
||||
**sg_attrs
|
||||
)
|
||||
|
||||
def delete_server_group(self, name_or_id):
|
||||
"""Delete a server group.
|
||||
|
@ -1446,12 +1407,7 @@ class ComputeCloudMixin(_normalize.Normalizer):
|
|||
name_or_id)
|
||||
return False
|
||||
|
||||
proxy._json_response(
|
||||
self.compute.delete(
|
||||
'/os-server-groups/{id}'.format(id=server_group['id'])),
|
||||
error_message="Error deleting server group {name}".format(
|
||||
name=name_or_id))
|
||||
|
||||
self.compute.delete_server_group(server_group, ignore_missing=False)
|
||||
return True
|
||||
|
||||
def create_flavor(self, name, ram, vcpus, disk, flavorid="auto",
|
||||
|
|
|
@ -623,18 +623,26 @@ class Proxy(proxy.Proxy):
|
|||
server = self._get_resource(_server.Server, server)
|
||||
server.revert_resize(self)
|
||||
|
||||
def create_server_image(self, server, name, metadata=None):
|
||||
def create_server_image(self, server, name, metadata=None, wait=False,
|
||||
timeout=120):
|
||||
"""Create an image from a server
|
||||
|
||||
:param server: Either the ID of a server or a
|
||||
:class:`~openstack.compute.v2.server.Server` instance.
|
||||
:class:`~openstack.compute.v2.server.Server` instance.
|
||||
:param str name: The name of the image to be created.
|
||||
:param dict metadata: A dictionary of metadata to be set on the image.
|
||||
|
||||
:returns: None
|
||||
:returns: :class:`~openstack.image.v2.image.Image` object.
|
||||
"""
|
||||
server = self._get_resource(_server.Server, server)
|
||||
server.create_image(self, name, metadata)
|
||||
image_id = server.create_image(self, name, metadata)
|
||||
|
||||
self._connection.list_images.invalidate(self)
|
||||
image = self._connection.get_image(image_id)
|
||||
|
||||
if not wait:
|
||||
return image
|
||||
return self._connection.wait_for_image(image, timeout=timeout)
|
||||
|
||||
def add_security_group_to_server(self, server, security_group):
|
||||
"""Add a security group to a server
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
from openstack.compute.v2 import metadata
|
||||
from openstack.image.v2 import image
|
||||
from openstack import exceptions
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
|
||||
|
@ -233,8 +234,10 @@ class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
|
|||
# the URL used is sans any additional /detail/ part.
|
||||
url = utils.urljoin(Server.base_path, self.id, 'action')
|
||||
headers = {'Accept': ''}
|
||||
return session.post(
|
||||
response = session.post(
|
||||
url, json=body, headers=headers, microversion=microversion)
|
||||
exceptions.raise_from_response(response)
|
||||
return response
|
||||
|
||||
def change_password(self, session, new_password):
|
||||
"""Change the administrator password to the given password."""
|
||||
|
@ -303,7 +306,39 @@ class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
|
|||
if metadata is not None:
|
||||
action['metadata'] = metadata
|
||||
body = {'createImage': action}
|
||||
self._action(session, body)
|
||||
|
||||
# You won't believe it - wait, who am I kidding - of course you will!
|
||||
# Nova returns the URL of the image created in the Location
|
||||
# header of the response. (what?) But, even better, the URL it responds
|
||||
# with has a very good chance of being wrong (it is built from
|
||||
# nova.conf values that point to internal API servers in any cloud
|
||||
# large enough to have both public and internal endpoints.
|
||||
# However, nobody has ever noticed this because novaclient doesn't
|
||||
# actually use that URL - it extracts the id from the end of
|
||||
# the url, then returns the id. This leads us to question:
|
||||
# a) why Nova is going to return a value in a header
|
||||
# b) why it's going to return data that probably broken
|
||||
# c) indeed the very nature of the fabric of reality
|
||||
# Although it fills us with existential dread, we have no choice but
|
||||
# to follow suit like a lemming being forced over a cliff by evil
|
||||
# producers from Disney.
|
||||
microversion = None
|
||||
if utils.supports_microversion(session, '2.45'):
|
||||
microversion = '2.45'
|
||||
response = self._action(session, body, microversion)
|
||||
|
||||
body = None
|
||||
try:
|
||||
# There might be body, might be not
|
||||
body = response.json()
|
||||
except Exception:
|
||||
pass
|
||||
if body and 'image_id' in body:
|
||||
image_id = body['image_id']
|
||||
else:
|
||||
image_id = response.headers['Location'].rsplit('/', 1)[1]
|
||||
|
||||
return image_id
|
||||
|
||||
def add_security_group(self, session, security_group):
|
||||
body = {"addSecurityGroup": {"name": security_group}}
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack import exceptions
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
|
||||
|
||||
class ServerGroup(resource.Resource):
|
||||
|
@ -20,6 +22,8 @@ class ServerGroup(resource.Resource):
|
|||
|
||||
_query_mapping = resource.QueryParameters("all_projects")
|
||||
|
||||
_max_microversion = '2.64'
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_fetch = True
|
||||
|
@ -29,9 +33,53 @@ class ServerGroup(resource.Resource):
|
|||
# Properties
|
||||
#: A name identifying the server group
|
||||
name = resource.Body('name')
|
||||
#: The list of policies supported by the server group
|
||||
#: The list of policies supported by the server group (till 2.63)
|
||||
policies = resource.Body('policies')
|
||||
#: The policy field represents the name of the policy (from 2.64)
|
||||
policy = resource.Body('policy')
|
||||
#: The list of members in the server group
|
||||
member_ids = resource.Body('members')
|
||||
#: The metadata associated with the server group
|
||||
metadata = resource.Body('metadata')
|
||||
#: The project ID who owns the server group.
|
||||
project_id = resource.Body('project_id')
|
||||
#: The rules field, which is a dict, can be applied to the policy
|
||||
rules = resource.Body('rules', type=list, list_type=dict)
|
||||
#: The user ID who owns the server group
|
||||
user_id = resource.Body('user_id')
|
||||
|
||||
def _get_microversion_for(self, session, action):
|
||||
"""Get microversion to use for the given action.
|
||||
|
||||
The base version uses :meth:`_get_microversion_for_list`.
|
||||
Subclasses can override this method if more complex logic is needed.
|
||||
|
||||
:param session: :class`keystoneauth1.adapter.Adapter`
|
||||
:param action: One of "fetch", "commit", "create", "delete", "patch".
|
||||
Unused in the base implementation.
|
||||
:return: microversion as string or ``None``
|
||||
"""
|
||||
if action not in ('fetch', 'commit', 'create', 'delete', 'patch'):
|
||||
raise ValueError('Invalid action: %s' % action)
|
||||
|
||||
microversion = self._get_microversion_for_list(session)
|
||||
if action == 'create':
|
||||
# `policy` and `rules` are added with mv=2.64. In it also
|
||||
# `policies` are removed.
|
||||
if utils.supports_microversion(session, '2.64'):
|
||||
if self.policies:
|
||||
if not self.policy and isinstance(self.policies, list):
|
||||
self.policy = self.policies[0]
|
||||
self.policies = None
|
||||
microversion = self._max_microversion
|
||||
else:
|
||||
if self.rules:
|
||||
message = ("API version %s is required to set rules, but "
|
||||
"it is not available.") % 2.64
|
||||
raise exceptions.NotSupported(message)
|
||||
if self.policy:
|
||||
if not self.policies:
|
||||
self.policies = [self.policy]
|
||||
self.policy = None
|
||||
|
||||
return microversion
|
||||
|
|
|
@ -33,6 +33,7 @@ class TestImageSnapshot(base.TestCase):
|
|||
snapshot_name = 'test-snapshot'
|
||||
fake_image = fakes.make_fake_image(self.image_id, status='pending')
|
||||
self.register_uris([
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(
|
||||
method='POST',
|
||||
uri='{endpoint}/servers/{server_id}/action'.format(
|
||||
|
@ -70,6 +71,7 @@ class TestImageSnapshot(base.TestCase):
|
|||
pending_image = fakes.make_fake_image(self.image_id, status='pending')
|
||||
fake_image = fakes.make_fake_image(self.image_id)
|
||||
self.register_uris([
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(
|
||||
method='POST',
|
||||
uri='{endpoint}/servers/{server_id}/action'.format(
|
||||
|
|
|
@ -36,11 +36,11 @@ class TestServerConsole(base.TestCase):
|
|||
id=self.server_id),
|
||||
json={"output": self.output},
|
||||
validate=dict(
|
||||
json={'os-getConsoleOutput': {'length': None}}))
|
||||
json={'os-getConsoleOutput': {'length': 5}}))
|
||||
])
|
||||
|
||||
self.assertEqual(
|
||||
self.output, self.cloud.get_server_console(self.server))
|
||||
self.output, self.cloud.get_server_console(self.server, 5))
|
||||
self.assert_calls()
|
||||
|
||||
def test_get_server_console_name_or_id(self):
|
||||
|
@ -57,7 +57,7 @@ class TestServerConsole(base.TestCase):
|
|||
id=self.server_id),
|
||||
json={"output": self.output},
|
||||
validate=dict(
|
||||
json={'os-getConsoleOutput': {'length': None}}))
|
||||
json={'os-getConsoleOutput': {}}))
|
||||
])
|
||||
|
||||
self.assertEqual(
|
||||
|
@ -74,7 +74,7 @@ class TestServerConsole(base.TestCase):
|
|||
id=self.server_id),
|
||||
status_code=400,
|
||||
validate=dict(
|
||||
json={'os-getConsoleOutput': {'length': None}}))
|
||||
json={'os-getConsoleOutput': {}}))
|
||||
])
|
||||
|
||||
self.assertEqual('', self.cloud.get_server_console(self.server))
|
||||
|
|
|
@ -30,6 +30,7 @@ class TestServerGroup(base.TestCase):
|
|||
def test_create_server_group(self):
|
||||
|
||||
self.register_uris([
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(method='POST',
|
||||
uri=self.get_mock_url(
|
||||
'compute', 'public', append=['os-server-groups']),
|
||||
|
@ -48,6 +49,7 @@ class TestServerGroup(base.TestCase):
|
|||
|
||||
def test_delete_server_group(self):
|
||||
self.register_uris([
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(method='GET',
|
||||
uri=self.get_mock_url(
|
||||
'compute', 'public', append=['os-server-groups']),
|
||||
|
|
|
@ -57,6 +57,7 @@ class TestServerSetMetadata(base.TestCase):
|
|||
self.assert_calls()
|
||||
|
||||
def test_server_set_metadata(self):
|
||||
metadata = {'meta': 'data'}
|
||||
self.register_uris([
|
||||
self.get_nova_discovery_mock_dict(),
|
||||
dict(method='GET',
|
||||
|
@ -67,10 +68,11 @@ class TestServerSetMetadata(base.TestCase):
|
|||
uri=self.get_mock_url(
|
||||
'compute', 'public',
|
||||
append=['servers', self.fake_server['id'], 'metadata']),
|
||||
validate=dict(json={'metadata': {'meta': 'data'}}),
|
||||
status_code=200),
|
||||
validate=dict(json={'metadata': metadata}),
|
||||
status_code=200,
|
||||
json={'metadata': metadata}),
|
||||
])
|
||||
|
||||
self.cloud.set_server_metadata(self.server_id, {'meta': 'data'})
|
||||
self.cloud.set_server_metadata(self.server_id, metadata)
|
||||
|
||||
self.assert_calls()
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import mock
|
||||
|
||||
from openstack.compute.v2 import _proxy
|
||||
from openstack.compute.v2 import availability_zone as az
|
||||
|
@ -448,6 +449,33 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
|
|||
method_args=["value", "key"],
|
||||
expected_args=[self.proxy, "key"])
|
||||
|
||||
def test_create_image(self):
|
||||
metadata = {'k1': 'v1'}
|
||||
with mock.patch('openstack.compute.v2.server.Server.create_image') \
|
||||
as ci_mock:
|
||||
|
||||
ci_mock.return_value = 'image_id'
|
||||
connection_mock = mock.Mock()
|
||||
connection_mock.get_image = mock.Mock(return_value='image')
|
||||
connection_mock.wait_for_image = mock.Mock()
|
||||
self.proxy._connection = connection_mock
|
||||
|
||||
rsp = self.proxy.create_server_image(
|
||||
'server', 'image_name', metadata, wait=True, timeout=1)
|
||||
|
||||
ci_mock.assert_called_with(
|
||||
self.proxy,
|
||||
'image_name',
|
||||
metadata
|
||||
)
|
||||
|
||||
self.proxy._connection.get_image.assert_called_with('image_id')
|
||||
self.proxy._connection.wait_for_image.assert_called_with(
|
||||
'image',
|
||||
timeout=1)
|
||||
|
||||
self.assertEqual(connection_mock.wait_for_image(), rsp)
|
||||
|
||||
def test_server_group_create(self):
|
||||
self.verify_create(self.proxy.create_server_group,
|
||||
server_group.ServerGroup)
|
||||
|
|
|
@ -124,6 +124,7 @@ class TestServer(base.TestCase):
|
|||
self.resp = mock.Mock()
|
||||
self.resp.body = None
|
||||
self.resp.json = mock.Mock(return_value=self.resp.body)
|
||||
self.resp.status_code = 200
|
||||
self.sess = mock.Mock()
|
||||
self.sess.post = mock.Mock(return_value=self.resp)
|
||||
|
||||
|
@ -395,30 +396,88 @@ class TestServer(base.TestCase):
|
|||
self.sess.post.assert_called_with(
|
||||
url, json=body, headers=headers, microversion=None)
|
||||
|
||||
def test_create_image(self):
|
||||
def test_create_image_header(self):
|
||||
sot = server.Server(**EXAMPLE)
|
||||
name = 'noo'
|
||||
metadata = {'nu': 'image', 'created': 'today'}
|
||||
|
||||
self.assertIsNone(sot.create_image(self.sess, name, metadata))
|
||||
url = 'servers/IDENTIFIER/action'
|
||||
body = {"createImage": {'name': name, 'metadata': metadata}}
|
||||
headers = {'Accept': ''}
|
||||
|
||||
rsp = mock.Mock()
|
||||
rsp.json.return_value = None
|
||||
rsp.headers = {'Location': 'dummy/dummy2'}
|
||||
rsp.status_code = 200
|
||||
|
||||
self.sess.post.return_value = rsp
|
||||
|
||||
self.endpoint_data = mock.Mock(spec=['min_microversion',
|
||||
'max_microversion'],
|
||||
min_microversion=None,
|
||||
max_microversion='2.44')
|
||||
self.sess.get_endpoint_data.return_value = self.endpoint_data
|
||||
|
||||
image_id = sot.create_image(self.sess, name, metadata)
|
||||
|
||||
self.sess.post.assert_called_with(
|
||||
url, json=body, headers=headers, microversion=None)
|
||||
|
||||
self.assertEqual('dummy2', image_id)
|
||||
|
||||
def test_create_image_microver(self):
|
||||
sot = server.Server(**EXAMPLE)
|
||||
name = 'noo'
|
||||
metadata = {'nu': 'image', 'created': 'today'}
|
||||
|
||||
url = 'servers/IDENTIFIER/action'
|
||||
body = {"createImage": {'name': name, 'metadata': metadata}}
|
||||
headers = {'Accept': ''}
|
||||
|
||||
rsp = mock.Mock()
|
||||
rsp.json.return_value = {'image_id': 'dummy3'}
|
||||
rsp.headers = {'Location': 'dummy/dummy2'}
|
||||
rsp.status_code = 200
|
||||
|
||||
self.sess.post.return_value = rsp
|
||||
|
||||
self.endpoint_data = mock.Mock(spec=['min_microversion',
|
||||
'max_microversion'],
|
||||
min_microversion='2.1',
|
||||
max_microversion='2.56')
|
||||
self.sess.get_endpoint_data.return_value = self.endpoint_data
|
||||
|
||||
image_id = sot.create_image(self.sess, name, metadata)
|
||||
|
||||
self.sess.post.assert_called_with(
|
||||
url, json=body, headers=headers, microversion=None)
|
||||
url, json=body, headers=headers, microversion='2.45')
|
||||
|
||||
self.assertEqual('dummy3', image_id)
|
||||
|
||||
def test_create_image_minimal(self):
|
||||
sot = server.Server(**EXAMPLE)
|
||||
name = 'noo'
|
||||
|
||||
self.assertIsNone(self.resp.body, sot.create_image(self.sess, name))
|
||||
|
||||
url = 'servers/IDENTIFIER/action'
|
||||
body = {"createImage": {'name': name}}
|
||||
headers = {'Accept': ''}
|
||||
|
||||
rsp = mock.Mock()
|
||||
rsp.json.return_value = None
|
||||
rsp.headers = {'Location': 'dummy/dummy2'}
|
||||
rsp.status_code = 200
|
||||
|
||||
self.sess.post.return_value = rsp
|
||||
|
||||
self.endpoint_data = mock.Mock(spec=['min_microversion',
|
||||
'max_microversion'],
|
||||
min_microversion='2.1',
|
||||
max_microversion='2.56')
|
||||
self.sess.get_endpoint_data.return_value = self.endpoint_data
|
||||
|
||||
self.assertIsNone(self.resp.body, sot.create_image(self.sess, name))
|
||||
|
||||
self.sess.post.assert_called_with(
|
||||
url, json=body, headers=headers, microversion=None)
|
||||
url, json=body, headers=headers, microversion='2.45')
|
||||
|
||||
def test_add_security_group(self):
|
||||
sot = server.Server(**EXAMPLE)
|
||||
|
|
Loading…
Reference in New Issue