Add support for injecting keypairs
Change-Id: I6aa4f8ca970ca70e7e2279e2436e94939e065e1f
This commit is contained in:
parent
0d00ed2fa8
commit
a7224e4ba9
@ -40,6 +40,7 @@ Request
|
|||||||
- networks.port_type: network_port_type
|
- networks.port_type: network_port_type
|
||||||
- user_data: user_data
|
- user_data: user_data
|
||||||
- personality: personality
|
- personality: personality
|
||||||
|
- key_name: key_name
|
||||||
|
|
||||||
**Example Create Instance: JSON request**
|
**Example Create Instance: JSON request**
|
||||||
|
|
||||||
|
@ -219,6 +219,12 @@ instance_uuid:
|
|||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
key_name:
|
||||||
|
description: |
|
||||||
|
Key pair name.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
keypair_fingerprint:
|
keypair_fingerprint:
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
|
@ -22,5 +22,6 @@
|
|||||||
"contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
"contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg=="
|
"user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
|
||||||
|
"key_name": "test_key"
|
||||||
}
|
}
|
||||||
|
@ -583,6 +583,7 @@ class InstanceController(InstanceControllerBase):
|
|||||||
instance_type_uuid = instance.get('instance_type_uuid')
|
instance_type_uuid = instance.get('instance_type_uuid')
|
||||||
image_uuid = instance.get('image_uuid')
|
image_uuid = instance.get('image_uuid')
|
||||||
user_data = instance.get('user_data')
|
user_data = instance.get('user_data')
|
||||||
|
key_name = instance.get('key_name')
|
||||||
personality = instance.pop('personality', None)
|
personality = instance.pop('personality', None)
|
||||||
|
|
||||||
injected_files = []
|
injected_files = []
|
||||||
@ -605,6 +606,7 @@ class InstanceController(InstanceControllerBase):
|
|||||||
requested_networks=requested_networks,
|
requested_networks=requested_networks,
|
||||||
user_data=user_data,
|
user_data=user_data,
|
||||||
injected_files=injected_files,
|
injected_files=injected_files,
|
||||||
|
key_name=key_name,
|
||||||
min_count=min_count,
|
min_count=min_count,
|
||||||
max_count=max_count)
|
max_count=max_count)
|
||||||
except exception.InstanceTypeNotFound:
|
except exception.InstanceTypeNotFound:
|
||||||
@ -616,6 +618,10 @@ class InstanceController(InstanceControllerBase):
|
|||||||
msg = (_("Requested image %s could not be found") % image_uuid)
|
msg = (_("Requested image %s could not be found") % image_uuid)
|
||||||
raise wsme.exc.ClientSideError(
|
raise wsme.exc.ClientSideError(
|
||||||
msg, status_code=http_client.BAD_REQUEST)
|
msg, status_code=http_client.BAD_REQUEST)
|
||||||
|
except exception.KeypairNotFound:
|
||||||
|
msg = (_("Invalid key_name %s provided.") % key_name)
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
msg, status_code=http_client.BAD_REQUEST)
|
||||||
except exception.PortLimitExceeded as e:
|
except exception.PortLimitExceeded as e:
|
||||||
raise wsme.exc.ClientSideError(
|
raise wsme.exc.ClientSideError(
|
||||||
e.message, status_code=http_client.FORBIDDEN)
|
e.message, status_code=http_client.FORBIDDEN)
|
||||||
|
@ -39,6 +39,7 @@ create_instance = {
|
|||||||
},
|
},
|
||||||
'user_data': {'type': 'string', 'format': 'base64'},
|
'user_data': {'type': 'string', 'format': 'base64'},
|
||||||
'personality': parameter_types.personality,
|
'personality': parameter_types.personality,
|
||||||
|
'key_name': parameter_types.name,
|
||||||
'min_count': {'type': 'integer', 'minimum': 1},
|
'min_count': {'type': 'integer', 'minimum': 1},
|
||||||
'max_count': {'type': 'integer', 'minimum': 1},
|
'max_count': {'type': 'integer', 'minimum': 1},
|
||||||
'extra': parameter_types.extra,
|
'extra': parameter_types.extra,
|
||||||
|
@ -79,7 +79,7 @@ class API(object):
|
|||||||
image_uuid, name, description,
|
image_uuid, name, description,
|
||||||
availability_zone, extra,
|
availability_zone, extra,
|
||||||
requested_networks, user_data,
|
requested_networks, user_data,
|
||||||
max_count):
|
key_name, max_count):
|
||||||
"""Verify all the input parameters"""
|
"""Verify all the input parameters"""
|
||||||
|
|
||||||
if user_data:
|
if user_data:
|
||||||
@ -100,6 +100,13 @@ class API(object):
|
|||||||
requested_networks,
|
requested_networks,
|
||||||
max_count)
|
max_count)
|
||||||
|
|
||||||
|
if key_name is not None:
|
||||||
|
key_pair = objects.KeyPair.get_by_name(context,
|
||||||
|
context.user_id,
|
||||||
|
key_name)
|
||||||
|
else:
|
||||||
|
key_pair = None
|
||||||
|
|
||||||
base_options = {
|
base_options = {
|
||||||
'image_uuid': image_uuid,
|
'image_uuid': image_uuid,
|
||||||
'status': states.BUILDING,
|
'status': states.BUILDING,
|
||||||
@ -114,7 +121,7 @@ class API(object):
|
|||||||
'availability_zone': availability_zone}
|
'availability_zone': availability_zone}
|
||||||
|
|
||||||
# return the validated options
|
# return the validated options
|
||||||
return base_options, max_network_count
|
return base_options, max_network_count, key_pair
|
||||||
|
|
||||||
def _new_instance_name_from_template(self, uuid, name, index):
|
def _new_instance_name_from_template(self, uuid, name, index):
|
||||||
"""Apply the template to instance name.
|
"""Apply the template to instance name.
|
||||||
@ -242,16 +249,18 @@ class API(object):
|
|||||||
def _create_instance(self, context, instance_type, image_uuid,
|
def _create_instance(self, context, instance_type, image_uuid,
|
||||||
name, description, availability_zone, extra,
|
name, description, availability_zone, extra,
|
||||||
requested_networks, user_data, injected_files,
|
requested_networks, user_data, injected_files,
|
||||||
min_count, max_count):
|
key_name, min_count, max_count):
|
||||||
"""Verify all the input parameters"""
|
"""Verify all the input parameters"""
|
||||||
|
|
||||||
# Verify the specified image exists
|
# Verify the specified image exists
|
||||||
if image_uuid:
|
if image_uuid:
|
||||||
self._get_image(context, image_uuid)
|
self._get_image(context, image_uuid)
|
||||||
|
|
||||||
base_options, max_net_count = self._validate_and_build_base_options(
|
base_options, max_net_count, key_pair = \
|
||||||
context, instance_type, image_uuid, name, description,
|
self._validate_and_build_base_options(
|
||||||
availability_zone, extra, requested_networks, user_data, max_count)
|
context, instance_type, image_uuid, name, description,
|
||||||
|
availability_zone, extra, requested_networks, user_data,
|
||||||
|
key_name, max_count)
|
||||||
|
|
||||||
# max_net_count is the maximum number of instances requested by the
|
# max_net_count is the maximum number of instances requested by the
|
||||||
# user adjusted for any network quota constraints, including
|
# user adjusted for any network quota constraints, including
|
||||||
@ -289,6 +298,7 @@ class API(object):
|
|||||||
requested_networks,
|
requested_networks,
|
||||||
user_data,
|
user_data,
|
||||||
decoded_files,
|
decoded_files,
|
||||||
|
key_pair,
|
||||||
request_spec,
|
request_spec,
|
||||||
filter_properties=None)
|
filter_properties=None)
|
||||||
|
|
||||||
@ -297,7 +307,8 @@ class API(object):
|
|||||||
def create(self, context, instance_type, image_uuid,
|
def create(self, context, instance_type, image_uuid,
|
||||||
name=None, description=None, availability_zone=None,
|
name=None, description=None, availability_zone=None,
|
||||||
extra=None, requested_networks=None, user_data=None,
|
extra=None, requested_networks=None, user_data=None,
|
||||||
injected_files=None, min_count=None, max_count=None):
|
injected_files=None, key_name=None, min_count=None,
|
||||||
|
max_count=None):
|
||||||
"""Provision instances
|
"""Provision instances
|
||||||
|
|
||||||
Sending instance information to the engine and will handle
|
Sending instance information to the engine and will handle
|
||||||
@ -316,7 +327,8 @@ class API(object):
|
|||||||
image_uuid, name, description,
|
image_uuid, name, description,
|
||||||
availability_zone, extra,
|
availability_zone, extra,
|
||||||
requested_networks, user_data,
|
requested_networks, user_data,
|
||||||
injected_files, min_count, max_count)
|
injected_files, key_name,
|
||||||
|
min_count, max_count)
|
||||||
|
|
||||||
def _delete_instance(self, context, instance):
|
def _delete_instance(self, context, instance):
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class OnFailureRescheduleTask(flow_utils.MoganTask):
|
|||||||
def __init__(self, engine_rpcapi):
|
def __init__(self, engine_rpcapi):
|
||||||
requires = ['filter_properties', 'request_spec', 'instance',
|
requires = ['filter_properties', 'request_spec', 'instance',
|
||||||
'requested_networks', 'user_data', 'injected_files',
|
'requested_networks', 'user_data', 'injected_files',
|
||||||
'context']
|
'key_pair', 'context']
|
||||||
super(OnFailureRescheduleTask, self).__init__(addons=[ACTION],
|
super(OnFailureRescheduleTask, self).__init__(addons=[ACTION],
|
||||||
requires=requires)
|
requires=requires)
|
||||||
self.engine_rpcapi = engine_rpcapi
|
self.engine_rpcapi = engine_rpcapi
|
||||||
@ -69,7 +69,8 @@ class OnFailureRescheduleTask(flow_utils.MoganTask):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _reschedule(self, context, cause, request_spec, filter_properties,
|
def _reschedule(self, context, cause, request_spec, filter_properties,
|
||||||
instance, requested_networks, user_data, injected_files):
|
instance, requested_networks, user_data, injected_files,
|
||||||
|
key_pair):
|
||||||
"""Actions that happen during the rescheduling attempt occur here."""
|
"""Actions that happen during the rescheduling attempt occur here."""
|
||||||
|
|
||||||
create_instance = self.engine_rpcapi.create_instance
|
create_instance = self.engine_rpcapi.create_instance
|
||||||
@ -95,6 +96,7 @@ class OnFailureRescheduleTask(flow_utils.MoganTask):
|
|||||||
return create_instance(context, instance, requested_networks,
|
return create_instance(context, instance, requested_networks,
|
||||||
user_data=user_data,
|
user_data=user_data,
|
||||||
injected_files=injected_files,
|
injected_files=injected_files,
|
||||||
|
key_pair=key_pair,
|
||||||
request_spec=request_spec,
|
request_spec=request_spec,
|
||||||
filter_properties=filter_properties)
|
filter_properties=filter_properties)
|
||||||
|
|
||||||
@ -210,17 +212,17 @@ class GenerateConfigDriveTask(flow_utils.MoganTask):
|
|||||||
"""Generate ConfigDrive value the instance."""
|
"""Generate ConfigDrive value the instance."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
requires = ['instance', 'user_data', 'injected_files', 'configdrive',
|
requires = ['instance', 'user_data', 'injected_files', 'key_pair',
|
||||||
'context']
|
'configdrive', 'context']
|
||||||
super(GenerateConfigDriveTask, self).__init__(addons=[ACTION],
|
super(GenerateConfigDriveTask, self).__init__(addons=[ACTION],
|
||||||
requires=requires)
|
requires=requires)
|
||||||
|
|
||||||
def _generate_configdrive(self, context, instance, user_data=None,
|
def _generate_configdrive(self, context, instance, user_data=None,
|
||||||
files=None):
|
files=None, key_pair=None):
|
||||||
"""Generate a config drive."""
|
"""Generate a config drive."""
|
||||||
|
|
||||||
i_meta = instance_metadata.InstanceMetadata(
|
i_meta = instance_metadata.InstanceMetadata(
|
||||||
instance, content=files, user_data=user_data)
|
instance, content=files, user_data=user_data, key_pair=key_pair)
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile() as uncompressed:
|
with tempfile.NamedTemporaryFile() as uncompressed:
|
||||||
with configdrive.ConfigDriveBuilder(instance_md=i_meta) as cdb:
|
with configdrive.ConfigDriveBuilder(instance_md=i_meta) as cdb:
|
||||||
@ -236,12 +238,13 @@ class GenerateConfigDriveTask(flow_utils.MoganTask):
|
|||||||
compressed.seek(0)
|
compressed.seek(0)
|
||||||
return base64.b64encode(compressed.read())
|
return base64.b64encode(compressed.read())
|
||||||
|
|
||||||
def execute(self, context, instance, user_data, injected_files,
|
def execute(self, context, instance, user_data, injected_files, key_pair,
|
||||||
configdrive):
|
configdrive):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
configdrive['value'] = self._generate_configdrive(
|
configdrive['value'] = self._generate_configdrive(
|
||||||
context, instance, user_data=user_data, files=injected_files)
|
context, instance, user_data=user_data, files=injected_files,
|
||||||
|
key_pair=key_pair)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
msg = ("Failed to build configdrive: %s" %
|
msg = ("Failed to build configdrive: %s" %
|
||||||
@ -284,7 +287,8 @@ class CreateInstanceTask(flow_utils.MoganTask):
|
|||||||
|
|
||||||
|
|
||||||
def get_flow(context, manager, instance, requested_networks, user_data,
|
def get_flow(context, manager, instance, requested_networks, user_data,
|
||||||
injected_files, ports, request_spec, filter_properties):
|
injected_files, key_pair, ports, request_spec,
|
||||||
|
filter_properties):
|
||||||
|
|
||||||
"""Constructs and returns the manager entrypoint flow
|
"""Constructs and returns the manager entrypoint flow
|
||||||
|
|
||||||
@ -310,6 +314,7 @@ def get_flow(context, manager, instance, requested_networks, user_data,
|
|||||||
'requested_networks': requested_networks,
|
'requested_networks': requested_networks,
|
||||||
'user_data': user_data,
|
'user_data': user_data,
|
||||||
'injected_files': injected_files,
|
'injected_files': injected_files,
|
||||||
|
'key_pair': key_pair,
|
||||||
'ports': ports,
|
'ports': ports,
|
||||||
'configdrive': {}
|
'configdrive': {}
|
||||||
}
|
}
|
||||||
|
@ -342,7 +342,7 @@ class EngineManager(base_manager.BaseEngineManager):
|
|||||||
|
|
||||||
@wrap_instance_fault
|
@wrap_instance_fault
|
||||||
def create_instance(self, context, instance, requested_networks,
|
def create_instance(self, context, instance, requested_networks,
|
||||||
user_data, injected_files, request_spec=None,
|
user_data, injected_files, key_pair, request_spec=None,
|
||||||
filter_properties=None):
|
filter_properties=None):
|
||||||
"""Perform a deployment."""
|
"""Perform a deployment."""
|
||||||
LOG.debug("Starting instance...", instance=instance)
|
LOG.debug("Starting instance...", instance=instance)
|
||||||
@ -393,6 +393,7 @@ class EngineManager(base_manager.BaseEngineManager):
|
|||||||
requested_networks,
|
requested_networks,
|
||||||
user_data,
|
user_data,
|
||||||
injected_files,
|
injected_files,
|
||||||
|
key_pair,
|
||||||
node['ports'],
|
node['ports'],
|
||||||
request_spec,
|
request_spec,
|
||||||
filter_properties,
|
filter_properties,
|
||||||
|
@ -50,7 +50,8 @@ class InvalidMetadataPath(Exception):
|
|||||||
class InstanceMetadata(object):
|
class InstanceMetadata(object):
|
||||||
"""Instance metadata."""
|
"""Instance metadata."""
|
||||||
|
|
||||||
def __init__(self, instance, content=None, user_data=None, extra_md=None):
|
def __init__(self, instance, content=None, user_data=None,
|
||||||
|
key_pair=None, extra_md=None):
|
||||||
"""Creation of this object should basically cover all time consuming
|
"""Creation of this object should basically cover all time consuming
|
||||||
collection. Methods after that should not cause time delays due to
|
collection. Methods after that should not cause time delays due to
|
||||||
network operations or lengthy cpu operations.
|
network operations or lengthy cpu operations.
|
||||||
@ -75,6 +76,7 @@ class InstanceMetadata(object):
|
|||||||
self.uuid = instance.uuid
|
self.uuid = instance.uuid
|
||||||
self.content = {}
|
self.content = {}
|
||||||
self.files = []
|
self.files = []
|
||||||
|
self.keypair = key_pair
|
||||||
|
|
||||||
# 'content' is passed in from the configdrive code in
|
# 'content' is passed in from the configdrive code in
|
||||||
# mogan/engine/flows/create_instance.py. That's how we get the
|
# mogan/engine/flows/create_instance.py. That's how we get the
|
||||||
@ -111,6 +113,17 @@ class InstanceMetadata(object):
|
|||||||
if self.extra_md:
|
if self.extra_md:
|
||||||
metadata.update(self.extra_md)
|
metadata.update(self.extra_md)
|
||||||
|
|
||||||
|
if self.keypair:
|
||||||
|
metadata['public_keys'] = {
|
||||||
|
self.keypair.name: self.keypair.public_key,
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata['keys'] = [
|
||||||
|
{'name': self.keypair.name,
|
||||||
|
'type': self.keypair.type,
|
||||||
|
'data': self.keypair.public_key}
|
||||||
|
]
|
||||||
|
|
||||||
metadata['hostname'] = self.hostname
|
metadata['hostname'] = self.hostname
|
||||||
metadata['name'] = self.instance.name
|
metadata['name'] = self.instance.name
|
||||||
metadata['availability_zone'] = self.availability_zone
|
metadata['availability_zone'] = self.availability_zone
|
||||||
|
@ -50,7 +50,7 @@ class EngineAPI(object):
|
|||||||
serializer=serializer)
|
serializer=serializer)
|
||||||
|
|
||||||
def create_instance(self, context, instance, requested_networks,
|
def create_instance(self, context, instance, requested_networks,
|
||||||
user_data, injected_files, request_spec,
|
user_data, injected_files, key_pair, request_spec,
|
||||||
filter_properties):
|
filter_properties):
|
||||||
"""Signal to engine service to perform a deployment."""
|
"""Signal to engine service to perform a deployment."""
|
||||||
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
cctxt = self.client.prepare(topic=self.topic, server=CONF.host)
|
||||||
@ -58,6 +58,7 @@ class EngineAPI(object):
|
|||||||
requested_networks=requested_networks,
|
requested_networks=requested_networks,
|
||||||
user_data=user_data,
|
user_data=user_data,
|
||||||
injected_files=injected_files,
|
injected_files=injected_files,
|
||||||
|
key_pair=key_pair,
|
||||||
request_spec=request_spec,
|
request_spec=request_spec,
|
||||||
filter_properties=filter_properties)
|
filter_properties=filter_properties)
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
instance_type = self._create_instance_type()
|
instance_type = self._create_instance_type()
|
||||||
mock_check_nets.return_value = 3
|
mock_check_nets.return_value = 3
|
||||||
|
|
||||||
base_opts, max_network_count = \
|
base_opts, max_network_count, key_pair = \
|
||||||
self.engine_api._validate_and_build_base_options(
|
self.engine_api._validate_and_build_base_options(
|
||||||
self.context,
|
self.context,
|
||||||
instance_type=instance_type,
|
instance_type=instance_type,
|
||||||
@ -61,6 +61,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
extra={'k1', 'v1'},
|
extra={'k1', 'v1'},
|
||||||
requested_networks=None,
|
requested_networks=None,
|
||||||
user_data=None,
|
user_data=None,
|
||||||
|
key_name=None,
|
||||||
max_count=2)
|
max_count=2)
|
||||||
|
|
||||||
self.assertEqual('fake-user', base_opts['user_id'])
|
self.assertEqual('fake-user', base_opts['user_id'])
|
||||||
@ -69,6 +70,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
self.assertEqual(instance_type.uuid, base_opts['instance_type_uuid'])
|
self.assertEqual(instance_type.uuid, base_opts['instance_type_uuid'])
|
||||||
self.assertEqual({'k1', 'v1'}, base_opts['extra'])
|
self.assertEqual({'k1', 'v1'}, base_opts['extra'])
|
||||||
self.assertEqual('test_az', base_opts['availability_zone'])
|
self.assertEqual('test_az', base_opts['availability_zone'])
|
||||||
|
self.assertEqual(None, key_pair)
|
||||||
|
|
||||||
@mock.patch.object(objects.Instance, 'create')
|
@mock.patch.object(objects.Instance, 'create')
|
||||||
def test__provision_instances(self, mock_inst_create):
|
def test__provision_instances(self, mock_inst_create):
|
||||||
@ -109,7 +111,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
'availability_zone': 'test_az'}
|
'availability_zone': 'test_az'}
|
||||||
min_count = 1
|
min_count = 1
|
||||||
max_count = 2
|
max_count = 2
|
||||||
mock_validate.return_value = (base_options, max_count)
|
mock_validate.return_value = (base_options, max_count, None)
|
||||||
mock_get_image.side_effect = None
|
mock_get_image.side_effect = None
|
||||||
mock_create.return_value = mock.MagicMock()
|
mock_create.return_value = mock.MagicMock()
|
||||||
mock_list_az.return_value = {'availability_zones': ['test_az']}
|
mock_list_az.return_value = {'availability_zones': ['test_az']}
|
||||||
@ -136,7 +138,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
mock_validate.assert_called_once_with(
|
mock_validate.assert_called_once_with(
|
||||||
self.context, instance_type, 'fake-uuid', 'fake-name',
|
self.context, instance_type, 'fake-uuid', 'fake-name',
|
||||||
'fake-descritpion', 'test_az', {'k1', 'v1'}, requested_networks,
|
'fake-descritpion', 'test_az', {'k1', 'v1'}, requested_networks,
|
||||||
None, max_count)
|
None, None, max_count)
|
||||||
self.assertTrue(mock_create.called)
|
self.assertTrue(mock_create.called)
|
||||||
self.assertTrue(mock_get_image.called)
|
self.assertTrue(mock_get_image.called)
|
||||||
res = self.dbapi._get_quota_usages(self.context, self.project_id)
|
res = self.dbapi._get_quota_usages(self.context, self.project_id)
|
||||||
@ -180,7 +182,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
'availability_zone': 'test_az'}
|
'availability_zone': 'test_az'}
|
||||||
min_count = 11
|
min_count = 11
|
||||||
max_count = 20
|
max_count = 20
|
||||||
mock_validate.return_value = (base_options, max_count)
|
mock_validate.return_value = (base_options, max_count, None)
|
||||||
mock_get_image.side_effect = None
|
mock_get_image.side_effect = None
|
||||||
mock_list_az.return_value = {'availability_zones': ['test_az']}
|
mock_list_az.return_value = {'availability_zones': ['test_az']}
|
||||||
requested_networks = [{'uuid': 'fake'}]
|
requested_networks = [{'uuid': 'fake'}]
|
||||||
@ -198,6 +200,7 @@ class ComputeAPIUnitTest(base.DbTestCase):
|
|||||||
requested_networks,
|
requested_networks,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
min_count,
|
min_count,
|
||||||
max_count)
|
max_count)
|
||||||
|
|
||||||
|
@ -109,6 +109,7 @@ class RPCAPITestCase(base.DbTestCase):
|
|||||||
requested_networks=[],
|
requested_networks=[],
|
||||||
user_data=None,
|
user_data=None,
|
||||||
injected_files=None,
|
injected_files=None,
|
||||||
|
key_pair=None,
|
||||||
request_spec=None,
|
request_spec=None,
|
||||||
filter_properties=None)
|
filter_properties=None)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user