Convert Server to new Schema format
blueprint property-schema-conversion Change-Id: I2da66244760f782e774b332b07b08e526ce5a7ba
This commit is contained in:
parent
36c0382d64
commit
f574a01e9a
@ -21,6 +21,8 @@ from heat.common import exception
|
|||||||
from heat.engine import clients
|
from heat.engine import clients
|
||||||
from heat.engine import scheduler
|
from heat.engine import scheduler
|
||||||
from heat.engine.resources import nova_utils
|
from heat.engine.resources import nova_utils
|
||||||
|
from heat.engine import constraints
|
||||||
|
from heat.engine import properties
|
||||||
from heat.engine import resource
|
from heat.engine import resource
|
||||||
from heat.openstack.common.gettextutils import _
|
from heat.openstack.common.gettextutils import _
|
||||||
from heat.openstack.common import log as logging
|
from heat.openstack.common import log as logging
|
||||||
@ -31,154 +33,202 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Server(resource.Resource):
|
class Server(resource.Resource):
|
||||||
|
|
||||||
block_mapping_schema = {
|
PROPERTIES = (
|
||||||
'device_name': {
|
NAME, IMAGE, BLOCK_DEVICE_MAPPING, FLAVOR,
|
||||||
'Type': 'String',
|
FLAVOR_UPDATE_POLICY, IMAGE_UPDATE_POLICY, KEY_NAME,
|
||||||
'Required': True,
|
ADMIN_USER, AVAILABILITY_ZONE, SECURITY_GROUPS, NETWORKS,
|
||||||
'Description': _('A device name where the volume will be '
|
SCHEDULER_HINTS, METADATA, USER_DATA_FORMAT, USER_DATA,
|
||||||
'attached in the system at /dev/device_name. '
|
RESERVATION_ID, CONFIG_DRIVE, DISK_CONFIG,
|
||||||
'This value is typically vda.')},
|
) = (
|
||||||
'volume_id': {
|
'name', 'image', 'block_device_mapping', 'flavor',
|
||||||
'Type': 'String',
|
'flavor_update_policy', 'image_update_policy', 'key_name',
|
||||||
'Description': _('The ID of the volume to boot from. Only one of '
|
'admin_user', 'availability_zone', 'security_groups', 'networks',
|
||||||
'volume_id or snapshot_id should be provided.')},
|
'scheduler_hints', 'metadata', 'user_data_format', 'user_data',
|
||||||
'snapshot_id': {
|
'reservation_id', 'config_drive', 'diskConfig',
|
||||||
'Type': 'String',
|
)
|
||||||
'Description': _('The ID of the snapshot to create a volume '
|
|
||||||
'from.')},
|
|
||||||
'volume_size': {
|
|
||||||
'Type': 'String',
|
|
||||||
'Description': _('The size of the volume, in GB. It is safe to '
|
|
||||||
'leave this blank and have the Compute service '
|
|
||||||
'infer the size.')},
|
|
||||||
'delete_on_termination': {
|
|
||||||
'Type': 'Boolean',
|
|
||||||
'Description': _('Indicate whether the volume should be deleted '
|
|
||||||
'when the server is terminated.')}
|
|
||||||
}
|
|
||||||
|
|
||||||
networks_schema = {
|
_BLOCK_DEVICE_MAPPING_KEYS = (
|
||||||
'uuid': {
|
BLOCK_DEVICE_MAPPING_DEVICE_NAME, BLOCK_DEVICE_MAPPING_VOLUME_ID,
|
||||||
'Type': 'String',
|
BLOCK_DEVICE_MAPPING_SNAPSHOT_ID,
|
||||||
'Description': _('DEPRECATED! ID of network '
|
BLOCK_DEVICE_MAPPING_VOLUME_SIZE,
|
||||||
'to create a port on.')},
|
BLOCK_DEVICE_MAPPING_DELETE_ON_TERM,
|
||||||
'network': {
|
) = (
|
||||||
'Type': 'String',
|
'device_name', 'volume_id',
|
||||||
'Description': _('Name or ID of network to create a port on.')},
|
'snapshot_id',
|
||||||
'fixed_ip': {
|
'volume_size',
|
||||||
'Type': 'String',
|
'delete_on_termination',
|
||||||
'Description': _('Fixed IP address to specify for the port '
|
)
|
||||||
'created on the requested network.')},
|
|
||||||
'port': {
|
_NETWORK_KEYS = (
|
||||||
'Type': 'String',
|
NETWORK_UUID, NETWORK_ID, NETWORK_FIXED_IP, NETWORK_PORT,
|
||||||
'Description': _('ID of an existing port to associate with '
|
) = (
|
||||||
'this server.')},
|
'uuid', 'network', 'fixed_ip', 'port',
|
||||||
}
|
)
|
||||||
|
|
||||||
properties_schema = {
|
properties_schema = {
|
||||||
'name': {
|
NAME: properties.Schema(
|
||||||
'Type': 'String',
|
properties.Schema.STRING,
|
||||||
'Description': _('Server name.')},
|
_('Server name.')
|
||||||
'image': {
|
),
|
||||||
'Type': 'String',
|
IMAGE: properties.Schema(
|
||||||
'Description': _('The ID or name of the image to boot with.'),
|
properties.Schema.STRING,
|
||||||
'UpdateAllowed': True},
|
_('The ID or name of the image to boot with.'),
|
||||||
'block_device_mapping': {
|
update_allowed=True
|
||||||
'Type': 'List',
|
),
|
||||||
'Description': _('Block device mappings for this server.'),
|
BLOCK_DEVICE_MAPPING: properties.Schema(
|
||||||
'Schema': {
|
properties.Schema.LIST,
|
||||||
'Type': 'Map',
|
_('Block device mappings for this server.'),
|
||||||
'Schema': block_mapping_schema
|
schema=properties.Schema(
|
||||||
}
|
properties.Schema.MAP,
|
||||||
},
|
schema={
|
||||||
'flavor': {
|
BLOCK_DEVICE_MAPPING_DEVICE_NAME: properties.Schema(
|
||||||
'Type': 'String',
|
properties.Schema.STRING,
|
||||||
'Description': _('The ID or name of the flavor to boot onto.'),
|
_('A device name where the volume will be '
|
||||||
'Required': True,
|
'attached in the system at /dev/device_name. '
|
||||||
'UpdateAllowed': True},
|
'This value is typically vda.'),
|
||||||
'flavor_update_policy': {
|
required=True
|
||||||
'Type': 'String',
|
),
|
||||||
'Description': _('Policy on how to apply a flavor update; either '
|
BLOCK_DEVICE_MAPPING_VOLUME_ID: properties.Schema(
|
||||||
'by requesting a server resize or by replacing '
|
properties.Schema.STRING,
|
||||||
'the entire server.'),
|
_('The ID of the volume to boot from. Only one '
|
||||||
'Default': 'RESIZE',
|
'of volume_id or snapshot_id should be '
|
||||||
'AllowedValues': ['RESIZE', 'REPLACE'],
|
'provided.')
|
||||||
'UpdateAllowed': True},
|
),
|
||||||
'image_update_policy': {
|
BLOCK_DEVICE_MAPPING_SNAPSHOT_ID: properties.Schema(
|
||||||
'Type': 'String',
|
properties.Schema.STRING,
|
||||||
'Default': 'REPLACE',
|
_('The ID of the snapshot to create a volume '
|
||||||
'Description': _('Policy on how to apply an image-id update; '
|
'from.')
|
||||||
'either by requesting a server rebuild or by '
|
),
|
||||||
'replacing the entire server'),
|
BLOCK_DEVICE_MAPPING_VOLUME_SIZE: properties.Schema(
|
||||||
'AllowedValues': ['REBUILD', 'REPLACE',
|
properties.Schema.STRING,
|
||||||
'REBUILD_PRESERVE_EPHEMERAL'],
|
_('The size of the volume, in GB. It is safe to '
|
||||||
'UpdateAllowed': True},
|
'leave this blank and have the Compute service '
|
||||||
'key_name': {
|
'infer the size.')
|
||||||
'Type': 'String',
|
),
|
||||||
'Description': _('Name of keypair to inject into the server.')},
|
BLOCK_DEVICE_MAPPING_DELETE_ON_TERM: properties.Schema(
|
||||||
'admin_user': {
|
properties.Schema.BOOLEAN,
|
||||||
'Type': 'String',
|
_('Indicate whether the volume should be deleted '
|
||||||
'Default': cfg.CONF.instance_user,
|
'when the server is terminated.')
|
||||||
'Description': _('Name of the administrative user to use '
|
),
|
||||||
'on the server.')},
|
},
|
||||||
'availability_zone': {
|
)
|
||||||
'Type': 'String',
|
),
|
||||||
'Description': _('Name of the availability zone for server '
|
FLAVOR: properties.Schema(
|
||||||
'placement.')},
|
properties.Schema.STRING,
|
||||||
'security_groups': {
|
_('The ID or name of the flavor to boot onto.'),
|
||||||
'Type': 'List',
|
required=True,
|
||||||
'Description': _('List of security group names or IDs.'),
|
update_allowed=True
|
||||||
'Default': []},
|
),
|
||||||
'networks': {
|
FLAVOR_UPDATE_POLICY: properties.Schema(
|
||||||
'Type': 'List',
|
properties.Schema.STRING,
|
||||||
'Description': _('An ordered list of nics to be '
|
_('Policy on how to apply a flavor update; either by requesting '
|
||||||
'added to this server, with information about '
|
'a server resize or by replacing the entire server.'),
|
||||||
'connected networks, fixed ips, port etc.'),
|
default='RESIZE',
|
||||||
'Schema': {
|
constraints=[
|
||||||
'Type': 'Map',
|
constraints.AllowedValues(['RESIZE', 'REPLACE']),
|
||||||
'Schema': networks_schema
|
],
|
||||||
}
|
update_allowed=True
|
||||||
},
|
),
|
||||||
'scheduler_hints': {
|
IMAGE_UPDATE_POLICY: properties.Schema(
|
||||||
'Type': 'Map',
|
properties.Schema.STRING,
|
||||||
'Description': _('Arbitrary key-value pairs specified by the '
|
_('Policy on how to apply an image-id update; either by '
|
||||||
'client to help boot a server.')},
|
'requesting a server rebuild or by replacing the entire server'),
|
||||||
'metadata': {
|
default='REPLACE',
|
||||||
'Type': 'Map',
|
constraints=[
|
||||||
'UpdateAllowed': True,
|
constraints.AllowedValues(['REBUILD', 'REPLACE',
|
||||||
'Description': _('Arbitrary key/value metadata to store for this '
|
'REBUILD_PRESERVE_EPHEMERAL']),
|
||||||
'server. Both keys and values must be 255 '
|
],
|
||||||
'characters or less.')},
|
update_allowed=True
|
||||||
'user_data_format': {
|
),
|
||||||
'Type': 'String',
|
KEY_NAME: properties.Schema(
|
||||||
'Default': 'HEAT_CFNTOOLS',
|
properties.Schema.STRING,
|
||||||
'Description': _('How the user_data should be formatted for the '
|
_('Name of keypair to inject into the server.')
|
||||||
'server. For HEAT_CFNTOOLS, the user_data is '
|
),
|
||||||
'bundled as part of the heat-cfntools '
|
ADMIN_USER: properties.Schema(
|
||||||
'cloud-init boot configuration data. For RAW, '
|
properties.Schema.STRING,
|
||||||
'the user_data is passed to Nova unmodified.'),
|
_('Name of the administrative user to use on the server.'),
|
||||||
'AllowedValues': ['HEAT_CFNTOOLS', 'RAW']},
|
default=cfg.CONF.instance_user
|
||||||
'user_data': {
|
),
|
||||||
'Type': 'String',
|
AVAILABILITY_ZONE: properties.Schema(
|
||||||
'Description': _('User data script to be executed by '
|
properties.Schema.STRING,
|
||||||
'cloud-init.'),
|
_('Name of the availability zone for server placement.')
|
||||||
'Default': ""},
|
),
|
||||||
'reservation_id': {
|
SECURITY_GROUPS: properties.Schema(
|
||||||
'Type': 'String',
|
properties.Schema.LIST,
|
||||||
'Description': _('A UUID for the set of servers being requested.')
|
_('List of security group names or IDs.'),
|
||||||
},
|
default=[]
|
||||||
'config_drive': {
|
),
|
||||||
'Type': 'String',
|
NETWORKS: properties.Schema(
|
||||||
'Description': _('value for config drive either boolean, or '
|
properties.Schema.LIST,
|
||||||
'volume-id.')
|
_('An ordered list of nics to be added to this server, with '
|
||||||
},
|
'information about connected networks, fixed ips, port etc.'),
|
||||||
# diskConfig translates to API attribute OS-DCF:diskConfig
|
schema=properties.Schema(
|
||||||
# hence the camel case instead of underscore to separate the words
|
properties.Schema.MAP,
|
||||||
'diskConfig': {
|
schema={
|
||||||
'Type': 'String',
|
NETWORK_UUID: properties.Schema(
|
||||||
'Description': _('Control how the disk is partitioned when the '
|
properties.Schema.STRING,
|
||||||
'server is created.'),
|
_('DEPRECATED! ID of network to create a port on.'),
|
||||||
'AllowedValues': ['AUTO', 'MANUAL']}
|
),
|
||||||
|
NETWORK_ID: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('Name or ID of network to create a port on.')
|
||||||
|
),
|
||||||
|
NETWORK_FIXED_IP: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('Fixed IP address to specify for the port '
|
||||||
|
'created on the requested network.')
|
||||||
|
),
|
||||||
|
NETWORK_PORT: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('ID of an existing port to associate with this '
|
||||||
|
'server.')
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
SCHEDULER_HINTS: properties.Schema(
|
||||||
|
properties.Schema.MAP,
|
||||||
|
_('Arbitrary key-value pairs specified by the client to help '
|
||||||
|
'boot a server.')
|
||||||
|
),
|
||||||
|
METADATA: properties.Schema(
|
||||||
|
properties.Schema.MAP,
|
||||||
|
_('Arbitrary key/value metadata to store for this server. Both '
|
||||||
|
'keys and values must be 255 characters or less.'),
|
||||||
|
update_allowed=True
|
||||||
|
),
|
||||||
|
USER_DATA_FORMAT: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('How the user_data should be formatted for the server. For '
|
||||||
|
'HEAT_CFNTOOLS, the user_data is bundled as part of the '
|
||||||
|
'heat-cfntools cloud-init boot configuration data. For RAW, '
|
||||||
|
'the user_data is passed to Nova unmodified.'),
|
||||||
|
default='HEAT_CFNTOOLS',
|
||||||
|
constraints=[
|
||||||
|
constraints.AllowedValues(['HEAT_CFNTOOLS', 'RAW']),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
USER_DATA: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('User data script to be executed by cloud-init.'),
|
||||||
|
default=''
|
||||||
|
),
|
||||||
|
RESERVATION_ID: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('A UUID for the set of servers being requested.')
|
||||||
|
),
|
||||||
|
CONFIG_DRIVE: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('value for config drive either boolean, or volume-id.')
|
||||||
|
),
|
||||||
|
DISK_CONFIG: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
_('Control how the disk is partitioned when the server is '
|
||||||
|
'created.'),
|
||||||
|
constraints=[
|
||||||
|
constraints.AllowedValues(['AUTO', 'MANUAL']),
|
||||||
|
]
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes_schema = {
|
attributes_schema = {
|
||||||
@ -210,48 +260,48 @@ class Server(resource.Resource):
|
|||||||
super(Server, self).__init__(name, json_snippet, stack)
|
super(Server, self).__init__(name, json_snippet, stack)
|
||||||
|
|
||||||
def physical_resource_name(self):
|
def physical_resource_name(self):
|
||||||
name = self.properties.get('name')
|
name = self.properties.get(self.NAME)
|
||||||
if name:
|
if name:
|
||||||
return name
|
return name
|
||||||
|
|
||||||
return super(Server, self).physical_resource_name()
|
return super(Server, self).physical_resource_name()
|
||||||
|
|
||||||
def handle_create(self):
|
def handle_create(self):
|
||||||
security_groups = self.properties.get('security_groups')
|
security_groups = self.properties.get(self.SECURITY_GROUPS)
|
||||||
|
|
||||||
user_data_format = self.properties.get('user_data_format')
|
user_data_format = self.properties.get(self.USER_DATA_FORMAT)
|
||||||
userdata = nova_utils.build_userdata(
|
userdata = nova_utils.build_userdata(
|
||||||
self,
|
self,
|
||||||
self.properties.get('user_data'),
|
self.properties.get(self.USER_DATA),
|
||||||
instance_user=self.properties['admin_user'],
|
instance_user=self.properties[self.ADMIN_USER],
|
||||||
user_data_format=user_data_format)
|
user_data_format=user_data_format)
|
||||||
|
|
||||||
flavor = self.properties['flavor']
|
flavor = self.properties[self.FLAVOR]
|
||||||
availability_zone = self.properties['availability_zone']
|
availability_zone = self.properties[self.AVAILABILITY_ZONE]
|
||||||
|
|
||||||
key_name = self.properties['key_name']
|
key_name = self.properties[self.KEY_NAME]
|
||||||
if key_name:
|
if key_name:
|
||||||
# confirm keypair exists
|
# confirm keypair exists
|
||||||
nova_utils.get_keypair(self.nova(), key_name)
|
nova_utils.get_keypair(self.nova(), key_name)
|
||||||
|
|
||||||
image = self.properties.get('image')
|
image = self.properties.get(self.IMAGE)
|
||||||
if image:
|
if image:
|
||||||
image = nova_utils.get_image_id(self.nova(), image)
|
image = nova_utils.get_image_id(self.nova(), image)
|
||||||
|
|
||||||
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
|
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
|
||||||
|
|
||||||
instance_meta = self.properties.get('metadata')
|
instance_meta = self.properties.get(self.METADATA)
|
||||||
if instance_meta is not None:
|
if instance_meta is not None:
|
||||||
instance_meta = dict((key, str(value)) for (key, value) in
|
instance_meta = dict((key, str(value)) for (key, value) in
|
||||||
instance_meta.items())
|
instance_meta.items())
|
||||||
|
|
||||||
scheduler_hints = self.properties.get('scheduler_hints')
|
scheduler_hints = self.properties.get(self.SCHEDULER_HINTS)
|
||||||
nics = self._build_nics(self.properties.get('networks'))
|
nics = self._build_nics(self.properties.get(self.NETWORKS))
|
||||||
block_device_mapping = self._build_block_device_mapping(
|
block_device_mapping = self._build_block_device_mapping(
|
||||||
self.properties.get('block_device_mapping'))
|
self.properties.get(self.BLOCK_DEVICE_MAPPING))
|
||||||
reservation_id = self.properties.get('reservation_id')
|
reservation_id = self.properties.get(self.RESERVATION_ID)
|
||||||
config_drive = self.properties.get('config_drive')
|
config_drive = self.properties.get(self.CONFIG_DRIVE)
|
||||||
disk_config = self.properties.get('diskConfig')
|
disk_config = self.properties.get(self.DISK_CONFIG)
|
||||||
|
|
||||||
server = None
|
server = None
|
||||||
try:
|
try:
|
||||||
@ -303,26 +353,31 @@ class Server(resource.Resource):
|
|||||||
status=server.status))
|
status=server.status))
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
@staticmethod
|
@classmethod
|
||||||
def _build_block_device_mapping(bdm):
|
def _build_block_device_mapping(cls, bdm):
|
||||||
if not bdm:
|
if not bdm:
|
||||||
return None
|
return None
|
||||||
bdm_dict = {}
|
bdm_dict = {}
|
||||||
for mapping in bdm:
|
for mapping in bdm:
|
||||||
mapping_parts = []
|
mapping_parts = []
|
||||||
if mapping.get('snapshot_id'):
|
snapshot_id = mapping.get(cls.BLOCK_DEVICE_MAPPING_SNAPSHOT_ID)
|
||||||
mapping_parts.append(mapping.get('snapshot_id'))
|
if snapshot_id:
|
||||||
|
mapping_parts.append(snapshot_id)
|
||||||
mapping_parts.append('snap')
|
mapping_parts.append('snap')
|
||||||
else:
|
else:
|
||||||
mapping_parts.append(mapping.get('volume_id'))
|
volume_id = mapping.get(cls.BLOCK_DEVICE_MAPPING_VOLUME_ID)
|
||||||
|
mapping_parts.append(volume_id)
|
||||||
mapping_parts.append('')
|
mapping_parts.append('')
|
||||||
if (mapping.get('volume_size') or
|
|
||||||
mapping.get('delete_on_termination')):
|
|
||||||
|
|
||||||
mapping_parts.append(mapping.get('volume_size', '0'))
|
volume_size = mapping.get(cls.BLOCK_DEVICE_MAPPING_VOLUME_SIZE)
|
||||||
if mapping.get('delete_on_termination'):
|
delete = mapping.get(cls.BLOCK_DEVICE_MAPPING_DELETE_ON_TERM)
|
||||||
mapping_parts.append(str(mapping.get('delete_on_termination')))
|
if volume_size or delete:
|
||||||
bdm_dict[mapping.get('device_name')] = ':'.join(mapping_parts)
|
mapping_parts.append(volume_size or '0')
|
||||||
|
if delete:
|
||||||
|
mapping_parts.append(str(delete))
|
||||||
|
|
||||||
|
device_name = mapping.get(cls.BLOCK_DEVICE_MAPPING_DEVICE_NAME)
|
||||||
|
bdm_dict[device_name] = ':'.join(mapping_parts)
|
||||||
|
|
||||||
return bdm_dict
|
return bdm_dict
|
||||||
|
|
||||||
@ -334,19 +389,19 @@ class Server(resource.Resource):
|
|||||||
|
|
||||||
for net_data in networks:
|
for net_data in networks:
|
||||||
nic_info = {}
|
nic_info = {}
|
||||||
if net_data.get('uuid'):
|
if net_data.get(self.NETWORK_UUID):
|
||||||
nic_info['net-id'] = net_data['uuid']
|
nic_info['net-id'] = net_data[self.NETWORK_UUID]
|
||||||
label_or_uuid = net_data.get('network')
|
label_or_uuid = net_data.get(self.NETWORK_ID)
|
||||||
if label_or_uuid:
|
if label_or_uuid:
|
||||||
if uuidutils.is_uuid_like(label_or_uuid):
|
if uuidutils.is_uuid_like(label_or_uuid):
|
||||||
nic_info['net-id'] = label_or_uuid
|
nic_info['net-id'] = label_or_uuid
|
||||||
else:
|
else:
|
||||||
network = self.nova().networks.find(label=label_or_uuid)
|
network = self.nova().networks.find(label=label_or_uuid)
|
||||||
nic_info['net-id'] = network.id
|
nic_info['net-id'] = network.id
|
||||||
if net_data.get('fixed_ip'):
|
if net_data.get(self.NETWORK_FIXED_IP):
|
||||||
nic_info['v4-fixed-ip'] = net_data['fixed_ip']
|
nic_info['v4-fixed-ip'] = net_data[self.NETWORK_FIXED_IP]
|
||||||
if net_data.get('port'):
|
if net_data.get(self.NETWORK_PORT):
|
||||||
nic_info['port-id'] = net_data['port']
|
nic_info['port-id'] = net_data[self.NETWORK_PORT]
|
||||||
nics.append(nic_info)
|
nics.append(nic_info)
|
||||||
return nics
|
return nics
|
||||||
|
|
||||||
@ -375,22 +430,22 @@ class Server(resource.Resource):
|
|||||||
checkers = []
|
checkers = []
|
||||||
server = None
|
server = None
|
||||||
|
|
||||||
if 'metadata' in prop_diff:
|
if self.METADATA in prop_diff:
|
||||||
server = self.nova().servers.get(self.resource_id)
|
server = self.nova().servers.get(self.resource_id)
|
||||||
nova_utils.meta_update(self.nova(),
|
nova_utils.meta_update(self.nova(),
|
||||||
server,
|
server,
|
||||||
prop_diff['metadata'])
|
prop_diff[self.METADATA])
|
||||||
|
|
||||||
if 'flavor' in prop_diff:
|
if self.FLAVOR in prop_diff:
|
||||||
|
|
||||||
flavor_update_policy = (
|
flavor_update_policy = (
|
||||||
prop_diff.get('flavor_update_policy') or
|
prop_diff.get(self.FLAVOR_UPDATE_POLICY) or
|
||||||
self.properties.get('flavor_update_policy'))
|
self.properties.get(self.FLAVOR_UPDATE_POLICY))
|
||||||
|
|
||||||
if flavor_update_policy == 'REPLACE':
|
if flavor_update_policy == 'REPLACE':
|
||||||
raise resource.UpdateReplace(self.name)
|
raise resource.UpdateReplace(self.name)
|
||||||
|
|
||||||
flavor = prop_diff['flavor']
|
flavor = prop_diff[self.FLAVOR]
|
||||||
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
|
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
|
||||||
if not server:
|
if not server:
|
||||||
server = self.nova().servers.get(self.resource_id)
|
server = self.nova().servers.get(self.resource_id)
|
||||||
@ -398,13 +453,13 @@ class Server(resource.Resource):
|
|||||||
flavor_id)
|
flavor_id)
|
||||||
checkers.append(checker)
|
checkers.append(checker)
|
||||||
|
|
||||||
if 'image' in prop_diff:
|
if self.IMAGE in prop_diff:
|
||||||
image_update_policy = (
|
image_update_policy = (
|
||||||
prop_diff.get('image_update_policy') or
|
prop_diff.get(self.IMAGE_UPDATE_POLICY) or
|
||||||
self.properties.get('image_update_policy'))
|
self.properties.get(self.IMAGE_UPDATE_POLICY))
|
||||||
if image_update_policy == 'REPLACE':
|
if image_update_policy == 'REPLACE':
|
||||||
raise resource.UpdateReplace(self.name)
|
raise resource.UpdateReplace(self.name)
|
||||||
image = prop_diff['image']
|
image = prop_diff[self.IMAGE]
|
||||||
image_id = nova_utils.get_image_id(self.nova(), image)
|
image_id = nova_utils.get_image_id(self.nova(), image)
|
||||||
if not server:
|
if not server:
|
||||||
server = self.nova().servers.get(self.resource_id)
|
server = self.nova().servers.get(self.resource_id)
|
||||||
@ -445,28 +500,32 @@ class Server(resource.Resource):
|
|||||||
super(Server, self).validate()
|
super(Server, self).validate()
|
||||||
|
|
||||||
# check validity of key
|
# check validity of key
|
||||||
key_name = self.properties.get('key_name')
|
key_name = self.properties.get(self.KEY_NAME)
|
||||||
if key_name:
|
if key_name:
|
||||||
nova_utils.get_keypair(self.nova(), key_name)
|
nova_utils.get_keypair(self.nova(), key_name)
|
||||||
|
|
||||||
# either volume_id or snapshot_id needs to be specified, but not both
|
# either volume_id or snapshot_id needs to be specified, but not both
|
||||||
# for block device mapping.
|
# for block device mapping.
|
||||||
bdm = self.properties.get('block_device_mapping') or []
|
bdm = self.properties.get(self.BLOCK_DEVICE_MAPPING) or []
|
||||||
bootable_vol = False
|
bootable_vol = False
|
||||||
for mapping in bdm:
|
for mapping in bdm:
|
||||||
if mapping['device_name'] == 'vda':
|
device_name = mapping[self.BLOCK_DEVICE_MAPPING_DEVICE_NAME]
|
||||||
bootable_vol = True
|
if device_name == 'vda':
|
||||||
|
bootable_vol = True
|
||||||
|
|
||||||
if mapping.get('volume_id') and mapping.get('snapshot_id'):
|
volume_id = mapping.get(self.BLOCK_DEVICE_MAPPING_VOLUME_ID)
|
||||||
raise exception.ResourcePropertyConflict('volume_id',
|
snapshot_id = mapping.get(self.BLOCK_DEVICE_MAPPING_SNAPSHOT_ID)
|
||||||
'snapshot_id')
|
if volume_id and snapshot_id:
|
||||||
if not mapping.get('volume_id') and not mapping.get('snapshot_id'):
|
raise exception.ResourcePropertyConflict(
|
||||||
|
self.BLOCK_DEVICE_MAPPING_VOLUME_ID,
|
||||||
|
self.BLOCK_DEVICE_MAPPING_SNAPSHOT_ID)
|
||||||
|
if not volume_id and not snapshot_id:
|
||||||
msg = _('Either volume_id or snapshot_id must be specified for'
|
msg = _('Either volume_id or snapshot_id must be specified for'
|
||||||
' device mapping %s') % mapping['device_name']
|
' device mapping %s') % device_name
|
||||||
raise exception.StackValidationFailed(message=msg)
|
raise exception.StackValidationFailed(message=msg)
|
||||||
|
|
||||||
# make sure the image exists if specified.
|
# make sure the image exists if specified.
|
||||||
image = self.properties.get('image')
|
image = self.properties.get(self.IMAGE)
|
||||||
if image:
|
if image:
|
||||||
nova_utils.get_image_id(self.nova(), image)
|
nova_utils.get_image_id(self.nova(), image)
|
||||||
elif not image and not bootable_vol:
|
elif not image and not bootable_vol:
|
||||||
@ -475,22 +534,26 @@ class Server(resource.Resource):
|
|||||||
raise exception.StackValidationFailed(message=msg)
|
raise exception.StackValidationFailed(message=msg)
|
||||||
# network properties 'uuid' and 'network' shouldn't be used
|
# network properties 'uuid' and 'network' shouldn't be used
|
||||||
# both at once for all networks
|
# both at once for all networks
|
||||||
networks = self.properties.get('networks') or []
|
networks = self.properties.get(self.NETWORKS) or []
|
||||||
for network in networks:
|
for network in networks:
|
||||||
if network.get('uuid') and network.get('network'):
|
if network.get(self.NETWORK_UUID) and network.get(self.NETWORK_ID):
|
||||||
msg = _('Properties "uuid" and "network" are both set '
|
msg = _('Properties "%(uuid)s" and "%(id)s" are both set '
|
||||||
'to the network "%(network)s" for the server '
|
'to the network "%(network)s" for the server '
|
||||||
'"%(server)s". The "uuid" property is deprecated. '
|
'"%(server)s". The "%(uuid)s" property is deprecated. '
|
||||||
'Use only "network" property.'
|
'Use only "%(id)s" property.'
|
||||||
'') % dict(network=network['network'],
|
'') % dict(uuid=self.NETWORK_UUID,
|
||||||
|
id=self.NETWORK_ID,
|
||||||
|
network=network[self.NETWORK_ID],
|
||||||
server=self.name)
|
server=self.name)
|
||||||
raise exception.StackValidationFailed(message=msg)
|
raise exception.StackValidationFailed(message=msg)
|
||||||
elif network.get('uuid'):
|
elif network.get(self.NETWORK_UUID):
|
||||||
logger.info(_('For the server "%(server)s" the "uuid" '
|
logger.info(_('For the server "%(server)s" the "%(uuid)s" '
|
||||||
'property is set to network "%(network)s". '
|
'property is set to network "%(network)s". '
|
||||||
'"uuid" property is deprecated. Use "network" '
|
'"%(uuid)s" property is deprecated. Use '
|
||||||
'property instead.'
|
'"%(id)s" property instead.'
|
||||||
'') % dict(network=network['network'],
|
'') % dict(uuid=self.NETWORK_UUID,
|
||||||
|
id=self.NETWORK_ID,
|
||||||
|
network=network[self.NETWORK_ID],
|
||||||
server=self.name))
|
server=self.name))
|
||||||
|
|
||||||
# verify that the number of metadata entries is not greater
|
# verify that the number of metadata entries is not greater
|
||||||
|
Loading…
Reference in New Issue
Block a user