Add tags property to OS::Nova::Server
Add tags support for OS::Nova::Server - updatable property and corresponding attribute. Change-Id: Ic12da07e68e9f0dc5685d9f1b90567e8137efbd3 Closes-bug: #1620282
This commit is contained in:
parent
ce64d23e49
commit
25f44b5471
|
@ -58,14 +58,16 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
ADMIN_USER, AVAILABILITY_ZONE, SECURITY_GROUPS, NETWORKS,
|
||||
SCHEDULER_HINTS, METADATA, USER_DATA_FORMAT, USER_DATA,
|
||||
RESERVATION_ID, CONFIG_DRIVE, DISK_CONFIG, PERSONALITY,
|
||||
ADMIN_PASS, SOFTWARE_CONFIG_TRANSPORT, USER_DATA_UPDATE_POLICY
|
||||
ADMIN_PASS, SOFTWARE_CONFIG_TRANSPORT, USER_DATA_UPDATE_POLICY,
|
||||
TAGS
|
||||
) = (
|
||||
'name', 'image', 'block_device_mapping', 'block_device_mapping_v2',
|
||||
'flavor', 'flavor_update_policy', 'image_update_policy', 'key_name',
|
||||
'admin_user', 'availability_zone', 'security_groups', 'networks',
|
||||
'scheduler_hints', 'metadata', 'user_data_format', 'user_data',
|
||||
'reservation_id', 'config_drive', 'diskConfig', 'personality',
|
||||
'admin_pass', 'software_config_transport', 'user_data_update_policy'
|
||||
'admin_pass', 'software_config_transport', 'user_data_update_policy',
|
||||
'tags'
|
||||
)
|
||||
|
||||
_BLOCK_DEVICE_MAPPING_KEYS = (
|
||||
|
@ -128,10 +130,10 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
|
||||
ATTRIBUTES = (
|
||||
NAME_ATTR, ADDRESSES, NETWORKS_ATTR, FIRST_ADDRESS,
|
||||
INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS,
|
||||
INSTANCE_NAME, ACCESSIPV4, ACCESSIPV6, CONSOLE_URLS, TAGS_ATTR
|
||||
) = (
|
||||
'name', 'addresses', 'networks', 'first_address',
|
||||
'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls',
|
||||
'instance_name', 'accessIPv4', 'accessIPv6', 'console_urls', 'tags'
|
||||
)
|
||||
|
||||
# valid image Status
|
||||
|
@ -511,6 +513,13 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
_('The administrator password for the server.'),
|
||||
update_allowed=True
|
||||
),
|
||||
TAGS: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('Server tags. Supported since client version 2.26.'),
|
||||
support_status=support.SupportStatus(version='8.0.0'),
|
||||
schema=properties.Schema(properties.Schema.STRING),
|
||||
update_allowed=True
|
||||
)
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
|
@ -577,6 +586,11 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
support_status=support.SupportStatus(version='2015.1'),
|
||||
type=attributes.Schema.MAP
|
||||
),
|
||||
TAGS_ATTR: attributes.Schema(
|
||||
_('Tags from the server. Supported since client version 2.26.'),
|
||||
support_status=support.SupportStatus(version='8.0.0'),
|
||||
type=attributes.Schema.LIST
|
||||
)
|
||||
}
|
||||
|
||||
physical_resource_name_limit = cfg.CONF.max_server_name_length
|
||||
|
@ -903,12 +917,19 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
def check_create_complete(self, server_id):
|
||||
check = self.client_plugin()._check_active(server_id)
|
||||
if check:
|
||||
if self.properties[self.TAGS]:
|
||||
self._update_server_tags(self.properties[self.TAGS])
|
||||
self.store_external_ports()
|
||||
# Addresses binds to server not immediately, so we need to wait
|
||||
# until server is created and after that associate floating ip.
|
||||
self.floating_ips_nova_associate()
|
||||
return check
|
||||
|
||||
def _update_server_tags(self, tags):
|
||||
server = self.client().servers.get(self.resource_id)
|
||||
self.client(version=self.client_plugin().V2_26
|
||||
).servers.set_tags(server, tags)
|
||||
|
||||
def floating_ips_nova_associate(self):
|
||||
# If there is no neutron used, floating_ip still unassociated,
|
||||
# so need associate it with nova.
|
||||
|
@ -939,11 +960,21 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
raise exception.EntityNotFound(entity='Resource',
|
||||
name=self.name)
|
||||
raise
|
||||
|
||||
try:
|
||||
tag_server = self.client(
|
||||
version=self.client_plugin().V2_26
|
||||
).server.get(self.resource_id)
|
||||
except Exception as ex:
|
||||
LOG.warning('Cannot resolve tags for observe reality in case of '
|
||||
'unsupported minimal version tag support client')
|
||||
else:
|
||||
server_data['tags'] = tag_server.tag_list()
|
||||
return server, server_data
|
||||
|
||||
def parse_live_resource_data(self, resource_properties, resource_data):
|
||||
server, server_data = resource_data
|
||||
return {
|
||||
result = {
|
||||
# there's a risk that flavor id will be int type, so cast to str
|
||||
self.FLAVOR: six.text_type(server_data.get(self.FLAVOR)['id']),
|
||||
self.IMAGE: six.text_type(server_data.get(self.IMAGE)['id']),
|
||||
|
@ -951,6 +982,9 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
self.METADATA: server_data.get(self.METADATA),
|
||||
self.NETWORKS: self._get_live_networks(server, resource_properties)
|
||||
}
|
||||
if 'tags' in server_data:
|
||||
result.update({self.TAGS: server_data['tags']})
|
||||
return result
|
||||
|
||||
def _get_live_networks(self, server, props):
|
||||
reality_nets = self._add_port_for_address(server,
|
||||
|
@ -1141,6 +1175,13 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
return server.accessIPv6
|
||||
if name == self.CONSOLE_URLS:
|
||||
return self.client_plugin('nova').get_console_urls(server)
|
||||
if name == self.TAGS_ATTR:
|
||||
try:
|
||||
cv = self.client(
|
||||
version=self.client_plugin().V2_26)
|
||||
return cv.servers.tag_list(server)
|
||||
except exception.InvalidServiceVersion:
|
||||
return None
|
||||
|
||||
def add_dependencies(self, deps):
|
||||
super(Server, self).add_dependencies(deps)
|
||||
|
@ -1282,6 +1323,9 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
self.client_plugin().meta_update(server,
|
||||
prop_diff[self.METADATA])
|
||||
|
||||
if self.TAGS in prop_diff:
|
||||
self._update_server_tags(prop_diff[self.TAGS] or [])
|
||||
|
||||
if self.FLAVOR in prop_diff:
|
||||
updaters.extend(self._update_flavor(prop_diff))
|
||||
|
||||
|
@ -1498,6 +1542,16 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin,
|
|||
network.get(self.NETWORK_PORT) is not None)
|
||||
self._validate_network(network)
|
||||
|
||||
# Check if tags is allowed to use
|
||||
if self.properties[self.TAGS]:
|
||||
try:
|
||||
self.client(
|
||||
version=self.client_plugin().V2_26)
|
||||
except exception.InvalidServiceVersion as ex:
|
||||
msg = _('Cannot use "tags" property - nova does not support '
|
||||
'it: %s') % six.text_type(ex)
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
# retrieve provider's absolute limits if it will be needed
|
||||
metadata = self.properties[self.METADATA]
|
||||
personality = self.properties[self.PERSONALITY]
|
||||
|
|
|
@ -405,6 +405,7 @@ class ServersTest(common.HeatTestCase):
|
|||
self.patchobject(self.fc.servers, 'get', return_value=return_server)
|
||||
self.patchobject(return_server, 'interface_list',
|
||||
return_value=interfaces)
|
||||
self.patchobject(self.fc.servers, 'tag_list', return_value=['test'])
|
||||
public_ip = return_server.networks['public'][0]
|
||||
self.assertEqual('1234',
|
||||
server.FnGetAtt('addresses')['public'][0]['port'])
|
||||
|
@ -430,6 +431,14 @@ class ServersTest(common.HeatTestCase):
|
|||
|
||||
expected_name = utils.PhysName(stack_name, server.name)
|
||||
self.assertEqual(expected_name, server.FnGetAtt('name'))
|
||||
self.assertEqual(['test'], server.FnGetAtt('tags'))
|
||||
# test with unsupported version
|
||||
server.client = mock.Mock(side_effect=[
|
||||
self.fc,
|
||||
exception.InvalidServiceVersion(service='a', version='0')])
|
||||
if server.attributes._resolved_values.get('tags'):
|
||||
del server.attributes._resolved_values['tags']
|
||||
self.assertIsNone(server.FnGetAtt('tags'))
|
||||
|
||||
def test_server_create_metadata(self):
|
||||
stack_name = 'create_metadata_test_stack'
|
||||
|
@ -2890,6 +2899,29 @@ class ServersTest(common.HeatTestCase):
|
|||
return_value=self.mock_flavor)
|
||||
self.assertIsNone(server.validate())
|
||||
|
||||
def test_server_unsupported_microversion_tags(self):
|
||||
stack_name = 'srv_val_tags'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
||||
tmpl.t['Resources']['WebServer']['Properties']['tags'] = ['a']
|
||||
self.patchobject(nova.NovaClientPlugin, '_create',
|
||||
side_effect=[
|
||||
exception.InvalidServiceVersion(service='a',
|
||||
version='1')])
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = servers.Server('server_create_image_err',
|
||||
resource_defns['WebServer'], stack)
|
||||
self.patchobject(glance.GlanceClientPlugin, 'get_image',
|
||||
return_value=self.mock_image)
|
||||
self.patchobject(nova.NovaClientPlugin, 'get_flavor',
|
||||
return_value=self.mock_flavor)
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
server.validate)
|
||||
self.assertEqual("Property error: "
|
||||
"Resources.WebServer.Properties.key_name: Invalid "
|
||||
"service a version 1", six.text_type(exc))
|
||||
|
||||
def test_server_validate_too_many_personality(self):
|
||||
stack_name = 'srv_val'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
|
|
Loading…
Reference in New Issue