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:
Peter Razumovsky 2016-09-22 16:00:20 +03:00
parent ce64d23e49
commit 25f44b5471
2 changed files with 91 additions and 5 deletions

View File

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

View File

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