Merge "Creating instance fail when inject ssh key in cells mode"

This commit is contained in:
Jenkins 2015-09-14 22:19:03 +00:00 committed by Gerrit Code Review
commit 411769be8f
9 changed files with 222 additions and 5 deletions

View File

@ -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*)"

View File

@ -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,

View File

@ -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]

View File

@ -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()

View File

@ -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

View File

@ -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')

View File

@ -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."""

View File

@ -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')

View File

@ -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):