Merge "Creating instance fail when inject ssh key in cells mode"
This commit is contained in:
commit
411769be8f
@ -56,7 +56,6 @@ r="$r|(?:tempest\.api\.compute\.servers\.test_disk_config\.ServerDiskConfigTestJ
|
||||
r="$r|(?:tempest\.api\.compute\.servers\.test_disk_config\.ServerDiskConfigTestJSON\.test_resize_server_from_manual_to_auto*)"
|
||||
r="$r|(?:tempest\.api\.compute\.servers\.test_create_server\.ServersTestJSON\.test_create_server_with_scheduler_hint_group*)"
|
||||
r="$r|(?:tempest\.api\.compute\.servers\.test_create_server\.ServersTestManualDisk\.test_create_server_with_scheduler_hint_group*)"
|
||||
r="$r|(?:tempest\.api\.compute\.servers\.test_servers\.ServersTestJSON\.test_create_specify_keypair*)"
|
||||
r="$r|(?:tempest\.api\.compute\.servers\.test_virtual_interfaces\.VirtualInterfacesTestJSON\.test_list_virtual_interfaces*)"
|
||||
r="$r|(?:tempest\.api\.compute\.test_networks\.ComputeNetworksTest\.test_list_networks*)"
|
||||
r="$r|(?:tempest\.scenario\.test_minimum_basic\.TestMinimumBasicScenario\.test_minimum_basic_scenario*)"
|
||||
|
@ -31,6 +31,8 @@ from nova.api.ec2 import ec2utils
|
||||
from nova.api.metadata import password
|
||||
from nova import availability_zones as az
|
||||
from nova import block_device
|
||||
from nova.cells import opts as cells_opts
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova import context
|
||||
from nova import network
|
||||
from nova import objects
|
||||
@ -313,9 +315,15 @@ class InstanceMetadata(object):
|
||||
self.instance.key_name: self.instance.key_data
|
||||
}
|
||||
|
||||
keypair = keypair_obj.KeyPair.get_by_name(
|
||||
context.get_admin_context(), self.instance.user_id,
|
||||
self.instance.key_name)
|
||||
if cells_opts.get_cell_type() == 'compute':
|
||||
cells_api = cells_rpcapi.CellsAPI()
|
||||
keypair = cells_api.get_keypair_at_top(
|
||||
context.get_admin_context(), self.instance.user_id,
|
||||
self.instance.key_name)
|
||||
else:
|
||||
keypair = keypair_obj.KeyPair.get_by_name(
|
||||
context.get_admin_context(), self.instance.user_id,
|
||||
self.instance.key_name)
|
||||
metadata['keys'] = [
|
||||
{'name': keypair.name,
|
||||
'type': keypair.type,
|
||||
|
@ -76,7 +76,7 @@ class CellsManager(manager.Manager):
|
||||
Scheduling requests get passed to the scheduler class.
|
||||
"""
|
||||
|
||||
target = oslo_messaging.Target(version='1.36')
|
||||
target = oslo_messaging.Target(version='1.37')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
LOG.warning(_LW('The cells feature of Nova is considered experimental '
|
||||
@ -583,3 +583,19 @@ class CellsManager(manager.Manager):
|
||||
|
||||
def set_admin_password(self, ctxt, instance, new_pass):
|
||||
self.msg_runner.set_admin_password(ctxt, instance, new_pass)
|
||||
|
||||
def get_keypair_at_top(self, ctxt, user_id, name):
|
||||
responses = self.msg_runner.get_keypair_at_top(ctxt, user_id, name)
|
||||
keypairs = [resp.value for resp in responses if resp.value is not None]
|
||||
|
||||
if len(keypairs) == 0:
|
||||
return None
|
||||
elif len(keypairs) > 1:
|
||||
cell_names = ', '.join([resp.cell_name for resp in responses
|
||||
if resp.value is not None])
|
||||
LOG.warning(_LW("The same keypair name '%(name)s' exists in the "
|
||||
"following cells: %(cell_names)s. The keypair "
|
||||
"value from the first cell is returned."),
|
||||
{'name': name, 'cell_names': cell_names})
|
||||
|
||||
return keypairs[0]
|
||||
|
@ -1211,6 +1211,18 @@ class _BroadcastMessageMethods(_BaseMessageMethods):
|
||||
context = message.ctxt
|
||||
return self.compute_api.get_migrations(context, filters)
|
||||
|
||||
def get_keypair_at_top(self, message, user_id, name):
|
||||
"""Get keypair in API cells by name. Just return None if there is
|
||||
no match keypair.
|
||||
"""
|
||||
if not self._at_the_top():
|
||||
return
|
||||
|
||||
try:
|
||||
return objects.KeyPair.get_by_name(message.ctxt, user_id, name)
|
||||
except exception.KeypairNotFound:
|
||||
pass
|
||||
|
||||
|
||||
_CELL_MESSAGE_TYPE_TO_MESSAGE_CLS = {'targeted': _TargetedMessage,
|
||||
'broadcast': _BroadcastMessage,
|
||||
@ -1804,6 +1816,15 @@ class MessageRunner(object):
|
||||
self._instance_action(ctxt, instance, 'set_admin_password',
|
||||
extra_kwargs={'new_pass': new_pass})
|
||||
|
||||
def get_keypair_at_top(self, ctxt, user_id, name):
|
||||
"""Get Key Pair by name at top level cell."""
|
||||
message = _BroadcastMessage(self, ctxt,
|
||||
'get_keypair_at_top',
|
||||
dict(user_id=user_id, name=name),
|
||||
'up',
|
||||
need_response=True, run_locally=False)
|
||||
return message.process()
|
||||
|
||||
@staticmethod
|
||||
def get_message_types():
|
||||
return _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS.keys()
|
||||
|
@ -117,6 +117,7 @@ class CellsAPI(object):
|
||||
* 1.35 - Make instance_update_at_top, instance_destroy_at_top
|
||||
and instance_info_cache_update_at_top use instance objects
|
||||
* 1.36 - Added 'delete_type' parameter to terminate_instance()
|
||||
* 1.37 - Add get_keypair_at_top to fetch keypair from api cell
|
||||
'''
|
||||
|
||||
VERSION_ALIASES = {
|
||||
@ -630,3 +631,15 @@ class CellsAPI(object):
|
||||
cctxt = self.client.prepare(version='1.29')
|
||||
cctxt.cast(ctxt, 'set_admin_password', instance=instance,
|
||||
new_pass=new_pass)
|
||||
|
||||
def get_keypair_at_top(self, ctxt, user_id, name):
|
||||
if not CONF.cells.enable:
|
||||
return
|
||||
|
||||
cctxt = self.client.prepare(version='1.37')
|
||||
keypair = cctxt.call(ctxt, 'get_keypair_at_top', user_id=user_id,
|
||||
name=name)
|
||||
if keypair is None:
|
||||
raise exception.KeypairNotFound(user_id=user_id,
|
||||
name=name)
|
||||
return keypair
|
||||
|
@ -879,3 +879,27 @@ class CellsManagerClassTestCase(test.NoDBTestCase):
|
||||
instance='fake-instance', new_pass='fake-password')
|
||||
set_admin_password.assert_called_once_with(self.ctxt,
|
||||
'fake-instance', 'fake-password')
|
||||
|
||||
def test_get_keypair_at_top(self):
|
||||
keypairs = [self._get_fake_response('fake_keypair'),
|
||||
self._get_fake_response('fake_keypair2')]
|
||||
with mock.patch.object(self.msg_runner,
|
||||
'get_keypair_at_top',
|
||||
return_value=keypairs) as fake_get_keypair:
|
||||
response = self.cells_manager.get_keypair_at_top(self.ctxt,
|
||||
'fake_user_id',
|
||||
'fake_name')
|
||||
fake_get_keypair.assert_called_once_with(self.ctxt, 'fake_user_id',
|
||||
'fake_name')
|
||||
self.assertEqual('fake_keypair', response)
|
||||
|
||||
def test_get_keypair_at_top_with_empty_responses(self):
|
||||
with mock.patch.object(self.msg_runner,
|
||||
'get_keypair_at_top',
|
||||
return_value=[]) as fake_get_keypair:
|
||||
self.assertIsNone(
|
||||
self.cells_manager.get_keypair_at_top(self.ctxt,
|
||||
'fake_user_id',
|
||||
'fake_name'))
|
||||
fake_get_keypair.assert_called_once_with(self.ctxt, 'fake_user_id',
|
||||
'fake_name')
|
||||
|
@ -2079,6 +2079,48 @@ class CellsBroadcastMethodsTestCase(test.TestCase):
|
||||
self.assertIn(response.value_or_raise(), [migrations_from_cell1,
|
||||
migrations_from_cell2])
|
||||
|
||||
@mock.patch.object(objects.KeyPair, 'get_by_name',
|
||||
return_value='fake_keypair')
|
||||
def test_get_keypair_at_top(self, fake_get_by_name):
|
||||
user_id = 'fake_user_id'
|
||||
name = 'fake_keypair_name'
|
||||
responses = self.src_msg_runner.get_keypair_at_top(self.ctxt,
|
||||
user_id, name)
|
||||
fake_get_by_name.assert_called_once_with(self.ctxt, user_id, name)
|
||||
|
||||
for response in responses:
|
||||
if response.value is not None:
|
||||
self.assertEqual('fake_keypair', response.value)
|
||||
|
||||
@mock.patch.object(objects.KeyPair, 'get_by_name')
|
||||
def test_get_keypair_at_top_with_objects_exception(self, fake_get_by_name):
|
||||
user_id = 'fake_user_id'
|
||||
name = 'fake_keypair_name'
|
||||
keypair_exception = exception.KeypairNotFound(user_id=user_id,
|
||||
name=name)
|
||||
fake_get_by_name.side_effect = keypair_exception
|
||||
responses = self.src_msg_runner.get_keypair_at_top(self.ctxt,
|
||||
user_id,
|
||||
name)
|
||||
fake_get_by_name.assert_called_once_with(self.ctxt, user_id, name)
|
||||
|
||||
for response in responses:
|
||||
self.assertIsNone(response.value)
|
||||
|
||||
@mock.patch.object(messaging._BroadcastMessage, 'process')
|
||||
def test_get_keypair_at_top_with_process_response(self, fake_process):
|
||||
user_id = 'fake_user_id'
|
||||
name = 'fake_keypair_name'
|
||||
response = messaging.Response(self.ctxt, 'cell', 'keypair', False)
|
||||
other_response = messaging.Response(self.ctxt, 'cell',
|
||||
'fake_other_keypair', False)
|
||||
fake_process.return_value = [response, other_response]
|
||||
|
||||
responses = self.src_msg_runner.get_keypair_at_top(self.ctxt,
|
||||
user_id, name)
|
||||
fake_process.assert_called_once_with()
|
||||
self.assertEqual(fake_process.return_value, responses)
|
||||
|
||||
|
||||
class CellsPublicInterfacesTestCase(test.TestCase):
|
||||
"""Test case for the public interfaces into cells messaging."""
|
||||
|
@ -760,3 +760,25 @@ class CellsAPITestCase(test.NoDBTestCase):
|
||||
'new_pass': 'fake-password'}
|
||||
self._check_result(call_info, 'set_admin_password',
|
||||
expected_args, version='1.29')
|
||||
|
||||
def test_get_keypair_at_top(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
result = self.cells_rpcapi.get_keypair_at_top(self.fake_context,
|
||||
'fake_user_id', 'fake_name')
|
||||
|
||||
expected_args = {'user_id': 'fake_user_id',
|
||||
'name': 'fake_name'}
|
||||
self._check_result(call_info, 'get_keypair_at_top',
|
||||
expected_args, version='1.37')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_get_keypair_at_top_with_not_found(self):
|
||||
call_info = self._stub_rpc_method('call', None)
|
||||
self.assertRaises(exception.KeypairNotFound,
|
||||
self.cells_rpcapi.get_keypair_at_top,
|
||||
self.fake_context, 'fake_user_id', 'fake_name')
|
||||
|
||||
expected_args = {'user_id': 'fake_user_id',
|
||||
'name': 'fake_name'}
|
||||
self._check_result(call_info, 'get_keypair_at_top',
|
||||
expected_args, version='1.37')
|
||||
|
@ -57,6 +57,7 @@ CONF = cfg.CONF
|
||||
|
||||
USER_DATA_STRING = (b"This is an encoded string")
|
||||
ENCODE_USER_DATA_STRING = base64.b64encode(USER_DATA_STRING)
|
||||
FAKE_SEED = '7qtD24mpMR2'
|
||||
|
||||
|
||||
def fake_inst_obj(context):
|
||||
@ -94,6 +95,12 @@ def fake_inst_obj(context):
|
||||
return inst
|
||||
|
||||
|
||||
def fake_keypair_obj(name, data):
|
||||
return objects.KeyPair(name=name,
|
||||
type='fake_type',
|
||||
public_key=data)
|
||||
|
||||
|
||||
def return_non_existing_address(*args, **kwarg):
|
||||
raise exception.NotFound()
|
||||
|
||||
@ -154,6 +161,8 @@ class MetadataTestCase(test.TestCase):
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
self.instance = fake_inst_obj(self.context)
|
||||
self.flags(use_local=True, group='conductor')
|
||||
self.keypair = fake_keypair_obj(self.instance.key_name,
|
||||
self.instance.key_data)
|
||||
fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs)
|
||||
|
||||
def test_can_pickle_metadata(self):
|
||||
@ -361,6 +370,69 @@ class MetadataTestCase(test.TestCase):
|
||||
data = md.get_ec2_metadata(version='2009-04-04')
|
||||
self.assertEqual(data['meta-data']['local-ipv4'], expected_local)
|
||||
|
||||
@mock.patch.object(base64, 'b64encode', lambda data: FAKE_SEED)
|
||||
@mock.patch('nova.cells.rpcapi.CellsAPI.get_keypair_at_top')
|
||||
@mock.patch.object(objects.KeyPair, 'get_by_name')
|
||||
@mock.patch.object(jsonutils, 'dumps')
|
||||
def _test_as_json_with_options(self, mock_json_dumps,
|
||||
mock_keypair, mock_cells_keypair,
|
||||
is_cells=False, os_version=base.GRIZZLY):
|
||||
if is_cells:
|
||||
self.flags(enable=True, group='cells')
|
||||
self.flags(cell_type='compute', group='cells')
|
||||
mock_keypair = mock_cells_keypair
|
||||
|
||||
instance = self.instance
|
||||
keypair = self.keypair
|
||||
md = fake_InstanceMetadata(self.stubs, instance)
|
||||
|
||||
expected_metadata = {
|
||||
'uuid': md.uuid,
|
||||
'hostname': md._get_hostname(),
|
||||
'name': instance.display_name,
|
||||
'launch_index': instance.launch_index,
|
||||
'availability_zone': md.availability_zone,
|
||||
}
|
||||
if md.launch_metadata:
|
||||
expected_metadata['meta'] = md.launch_metadata
|
||||
if md.files:
|
||||
expected_metadata['files'] = md.files
|
||||
if md.extra_md:
|
||||
expected_metadata['extra_md'] = md.extra_md
|
||||
if md.network_config:
|
||||
expected_metadata['network_config'] = md.network_config
|
||||
if instance.key_name:
|
||||
expected_metadata['public_keys'] = {
|
||||
keypair.name: keypair.public_key
|
||||
}
|
||||
expected_metadata['keys'] = [{'type': keypair.type,
|
||||
'data': keypair.public_key,
|
||||
'name': keypair.name}]
|
||||
if md._check_os_version(base.GRIZZLY, os_version):
|
||||
expected_metadata['random_seed'] = FAKE_SEED
|
||||
if md._check_os_version(base.LIBERTY, os_version):
|
||||
expected_metadata['project_id'] = instance.project_id
|
||||
|
||||
mock_keypair.return_value = keypair
|
||||
md._metadata_as_json(os_version, 'non useless path parameter')
|
||||
if instance.key_name:
|
||||
mock_keypair.assert_called_once_with(mock.ANY,
|
||||
instance.user_id,
|
||||
instance.key_name)
|
||||
self.assertIsInstance(mock_keypair.call_args[0][0],
|
||||
context.RequestContext)
|
||||
self.assertEqual(md.md_mimetype, base.MIME_TYPE_APPLICATION_JSON)
|
||||
mock_json_dumps.assert_called_once_with(expected_metadata)
|
||||
|
||||
def test_as_json(self):
|
||||
for os_version in base.OPENSTACK_VERSIONS:
|
||||
self._test_as_json_with_options(os_version=os_version)
|
||||
|
||||
def test_as_json_with_cells_mode(self):
|
||||
for os_version in base.OPENSTACK_VERSIONS:
|
||||
self._test_as_json_with_options(is_cells=True,
|
||||
os_version=os_version)
|
||||
|
||||
|
||||
class OpenStackMetadataTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
|
Loading…
Reference in New Issue
Block a user