Add console_urls attribute to server resource
This is a composite attribute that returns a dict-like structure lazily resolving the actual console URL based on key supplied or dict of all available consoles if no key is provided. Example of usage to get only 'novnc' console: outputs: console: value: { get_attr: [server, console_urls, novnc ] } Change-Id: I27c0426f1a9100bb483f04c059fec87bb69b6340 Implements: blueprint vnc-console-attr Co-Authored-By: Jun Jie Nan <nanjj@cn.ibm.com>
This commit is contained in:
parent
951841adbf
commit
832eddbe27
|
@ -425,6 +425,41 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
|
|||
return dict([(limit.name, limit.value)
|
||||
for limit in list(limits.absolute)])
|
||||
|
||||
def get_console_urls(self, server):
|
||||
"""Return dict-like structure of server's console urls.
|
||||
|
||||
The actual console url is lazily resolved on access.
|
||||
|
||||
"""
|
||||
|
||||
class ConsoleUrls(collections.Mapping):
|
||||
def __init__(self, server):
|
||||
self.console_methods = {
|
||||
'novnc': server.get_vnc_console,
|
||||
'xvpvnc': server.get_vnc_console,
|
||||
'spice-html5': server.get_spice_console,
|
||||
'rdp-html5': server.get_rdp_console,
|
||||
}
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
url = self.console_methods[key](key)['console']['url']
|
||||
except exceptions.BadRequest as e:
|
||||
unavailable = 'Unavailable console type'
|
||||
if unavailable in e.message:
|
||||
url = e.message
|
||||
else:
|
||||
raise
|
||||
return url
|
||||
|
||||
def __len__(self):
|
||||
return len(self.console_methods)
|
||||
|
||||
def __iter__(self):
|
||||
return (key for key in self.console_methods)
|
||||
|
||||
return ConsoleUrls(server)
|
||||
|
||||
|
||||
class ServerConstraint(constraints.BaseCustomConstraint):
|
||||
|
||||
|
|
|
@ -87,10 +87,10 @@ class Server(stack_user.StackUser):
|
|||
|
||||
ATTRIBUTES = (
|
||||
NAME_ATTR, SHOW, ADDRESSES, NETWORKS_ATTR, FIRST_ADDRESS,
|
||||
INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6,
|
||||
INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS,
|
||||
) = (
|
||||
'name', 'show', 'addresses', 'networks', 'first_address',
|
||||
'instance_name', 'accessIPv4', 'accessIPv6',
|
||||
'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
|
@ -344,6 +344,14 @@ class Server(stack_user.StackUser):
|
|||
_('The manually assigned alternative public IPv6 address '
|
||||
'of the server.')
|
||||
),
|
||||
CONSOLE_URLS: attributes.Schema(
|
||||
_("URLs of server's consoles. "
|
||||
"To get a specific console type, the requested type "
|
||||
"can be specified as parameter to the get_attr function, "
|
||||
"e.g. get_attr: [ <server>, console_urls, novnc ]. "
|
||||
"Currently supported types are "
|
||||
"novnc, xvpvnc, spice-html5, rdp-html5.")
|
||||
),
|
||||
}
|
||||
|
||||
# Server host name limit to 53 characters by due to typical default
|
||||
|
@ -685,6 +693,8 @@ class Server(stack_user.StackUser):
|
|||
return server.accessIPv6
|
||||
if name == self.SHOW:
|
||||
return server._info
|
||||
if name == self.CONSOLE_URLS:
|
||||
return self.client_plugin('nova').get_console_urls(server)
|
||||
|
||||
def add_dependencies(self, deps):
|
||||
super(Server, self).add_dependencies(deps)
|
||||
|
|
|
@ -338,3 +338,68 @@ class KeypairConstraintTest(common.HeatTestCase):
|
|||
self.assertTrue(constraint.validate("", ctx))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class ConsoleUrlsTest(common.HeatTestCase):
|
||||
|
||||
scenarios = [
|
||||
('novnc', dict(console_type='novnc', srv_method='vnc')),
|
||||
('xvpvnc', dict(console_type='xvpvnc', srv_method='vnc')),
|
||||
('spice', dict(console_type='spice-html5', srv_method='spice')),
|
||||
('rdp', dict(console_type='rdp-html5', srv_method='rdp')),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(ConsoleUrlsTest, self).setUp()
|
||||
self.nova_client = mock.Mock()
|
||||
con = utils.dummy_context()
|
||||
c = con.clients
|
||||
self.nova_plugin = c.client_plugin('nova')
|
||||
self.nova_plugin._client = self.nova_client
|
||||
self.server = mock.Mock()
|
||||
self.console_method = getattr(self.server,
|
||||
'get_%s_console' % self.srv_method)
|
||||
|
||||
def test_get_console_url(self):
|
||||
console = {
|
||||
'console': {
|
||||
'type': self.console_type,
|
||||
'url': '%s_console_url' % self.console_type
|
||||
}
|
||||
}
|
||||
self.console_method.return_value = console
|
||||
|
||||
console_url = self.nova_plugin.get_console_urls(self.server)[
|
||||
self.console_type]
|
||||
|
||||
self.assertEqual(console['console']['url'], console_url)
|
||||
self.console_method.assert_called_once_with(self.console_type)
|
||||
|
||||
def test_get_console_url_tolerate_unavailable(self):
|
||||
msg = 'Unavailable console type %s.' % self.console_type
|
||||
self.console_method.side_effect = nova_exceptions.BadRequest(
|
||||
400, message=msg)
|
||||
|
||||
console_url = self.nova_plugin.get_console_urls(self.server)[
|
||||
self.console_type]
|
||||
|
||||
self.console_method.assert_called_once_with(self.console_type)
|
||||
self.assertEqual(msg, console_url)
|
||||
|
||||
def test_get_console_urls_reraises_other_400(self):
|
||||
exc = nova_exceptions.BadRequest
|
||||
self.console_method.side_effect = exc(400, message="spam")
|
||||
|
||||
urls = self.nova_plugin.get_console_urls(self.server)
|
||||
e = self.assertRaises(exc, urls.__getitem__, self.console_type)
|
||||
self.assertIn('spam', e.message)
|
||||
self.console_method.assert_called_once_with(self.console_type)
|
||||
|
||||
def test_get_console_urls_reraises_other(self):
|
||||
exc = Exception
|
||||
self.console_method.side_effect = exc("spam")
|
||||
|
||||
urls = self.nova_plugin.get_console_urls(self.server)
|
||||
e = self.assertRaises(exc, urls.__getitem__, self.console_type)
|
||||
self.assertIn('spam', e.args)
|
||||
self.console_method.assert_called_once_with(self.console_type)
|
||||
|
|
|
@ -1919,7 +1919,6 @@ class ServersTest(common.HeatTestCase):
|
|||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = servers.Server('server_create_image_err',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
server.validate)
|
||||
self.assertIn("Value '10a' is not an integer", six.text_type(exc))
|
||||
|
@ -2152,6 +2151,24 @@ class ServersTest(common.HeatTestCase):
|
|||
self.assertEqual(server._resolve_attribute("accessIPv4"), '')
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_resolve_attribute_console_url(self):
|
||||
server = self.fc.servers.list()[0]
|
||||
tmpl, stack = self._setup_test_stack('console_url_stack')
|
||||
ws = servers.Server(
|
||||
'WebServer', tmpl.resource_definitions(stack)['WebServer'], stack)
|
||||
ws.resource_id = server.id
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().AndReturn(self.fc)
|
||||
self.m.StubOutWithMock(self.fc.servers, 'get')
|
||||
self.fc.servers.get(server.id).AndReturn(server)
|
||||
self.m.ReplayAll()
|
||||
|
||||
console_urls = ws._resolve_attribute('console_urls')
|
||||
self.assertIsInstance(console_urls, collections.Mapping)
|
||||
supported_consoles = ('novnc', 'xvpvnc', 'spice-html5', 'rdp-html5')
|
||||
self.assertEqual(set(supported_consoles), set(console_urls.keys()))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_default_instance_user(self):
|
||||
"""The default value for instance_user in heat.conf is ec2-user."""
|
||||
return_server = self.fc.servers.list()[1]
|
||||
|
|
Loading…
Reference in New Issue