Merge pull request #29765 from gtmanfred/2015.8

allow nova driver to be boot from volume
This commit is contained in:
Mike Place 2015-12-17 11:20:33 -07:00
commit 1b430b251f
3 changed files with 187 additions and 19 deletions

View file

@ -82,6 +82,78 @@ accept them
- net-id: 00000000-0000-0000-0000-000000000000
- net-id: 11111111-1111-1111-1111-111111111111
This is an example profile.
.. code-block:: yaml
debian8-2-iad-cloudqe4:
provider: cloudqe4-iad
size: performance1-2
image: Debian 8 (Jessie) (PVHVM)
script_args: -UP -p python-zmq git 2015.8
and one using cinder volumes already attached
.. code-block:: yaml
# create the block storage device
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
block_device:
- source: image
id: <image_id>
dest: volume
size: 100
shutdown: <preserve/remove>
bootindex: 0
# with the volume already created
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
block_volume: <volume id>
# create the volume from a snapshot
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
snapshot: <cinder snapshot id>
# create the create an extra ephemeral disk
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
ephemeral:
- size: 100
format: <swap/ext4>
# create the create an extra ephemeral disk
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
swap: <size>
Block Device can also be used for having more than one block storage device attached
.. code-block:: yaml
centos7-2-iad-rackspace:
provider: rackspace-iad
size: general1-2
block_device:
- source: image
id: <image_id>
dest: volume
size: 100
shutdown: <preserve/remove>
bootindex: 0
- source: blank
dest: volume
device: xvdc
size: 100
shutdown: <preserve/remove>
Note: You must include the default net-ids when setting networks or the server
will be created without the rest of the interfaces
@ -228,11 +300,14 @@ def get_image(conn, vm_):
'''
Return the image object to use
'''
image_list = conn.image_list()
vm_image = config.get_cloud_config_value('image', vm_, __opts__).encode(
vm_image = config.get_cloud_config_value('image', vm_, __opts__, default='').encode(
'ascii', 'salt-cloud-force-ascii'
)
if not vm_image:
log.debug('No image set, must be boot from volume')
return None
image_list = conn.image_list()
for img in image_list:
if vm_image in (image_list[img]['id'], img):
@ -250,6 +325,17 @@ def get_image(conn, vm_):
)
def get_block_mapping_opts(vm_):
ret = {}
ret['block_device_mapping'] = config.get_cloud_config_value('block_device_mapping', vm_, __opts__, default={})
ret['block_device'] = config.get_cloud_config_value('block_device', vm_, __opts__, default=[])
ret['ephemeral'] = config.get_cloud_config_value('ephemeral', vm_, __opts__, default=[])
ret['swap'] = config.get_cloud_config_value('swap', vm_, __opts__, default=None)
ret['snapshot'] = config.get_cloud_config_value('snapshot', vm_, __opts__, default=None)
ret['boot_volume'] = config.get_cloud_config_value('boot_volume', vm_, __opts__, default=None)
return ret
def show_instance(name, call=None):
'''
Show the details from the provider concerning an instance
@ -525,12 +611,14 @@ def request_instance(vm_=None, call=None):
'config_drive', vm_, __opts__, search_global=False
)
kwargs.update(get_block_mapping_opts(vm_))
salt.utils.cloud.fire_event(
'event',
'requesting instance',
'salt/cloud/{0}/requesting'.format(vm_['name']),
{'kwargs': {'name': kwargs['name'],
'image': kwargs['image_id'],
'image': kwargs.get('image_id', 'Boot From Volume'),
'size': kwargs['flavor_id']}},
transport=__opts__['transport']
)
@ -661,6 +749,7 @@ def create(vm_):
for network in node['addresses'].get(networkname, []):
if network['version'] is 4:
node['extra']['access_ip'] = network['addr']
access_ip = network['addr']
break
vm_['cloudnetwork'] = True
@ -717,7 +806,7 @@ def create(vm_):
result.append(private_ip)
if rackconnect(vm_) is True and (ssh_interface(vm_) != 'private_ips' or rackconnectv3):
data.public_ips = access_ip
data.public_ips = [access_ip, ]
return data
# populate return data with private_ips

View file

@ -2575,23 +2575,27 @@ def is_profile_configured(opts, provider, profile_name):
alias, driver = provider.split(':')
# Most drivers need an image to be specified, but some do not.
non_image_drivers = ['vmware']
non_image_drivers = ['vmware', 'nova']
# Most drivers need a size, but some do not.
non_size_drivers = ['opennebula', 'parallels', 'proxmox', 'scaleway',
'softlayer', 'softlayer_hw', 'vmware', 'vsphere']
provider_key = opts['providers'][alias][driver]
profile_key = opts['providers'][alias][driver]['profiles'][profile_name]
if driver not in non_image_drivers:
required_keys.append('image')
elif driver == 'vmware':
required_keys.append('clonefrom')
elif driver == 'nova':
nova_image_keys = ['image', 'block_device_mapping', 'block_device']
if not any([key in provider_key for key in nova_image_keys]) and not any([key in profile_key for key in nova_image_keys]):
required_keys.extend(nova_image_keys)
if driver not in non_size_drivers:
required_keys.append('size')
provider_key = opts['providers'][alias][driver]
profile_key = opts['providers'][alias][driver]['profiles'][profile_name]
# Check if image and/or size are supplied in the provider config. If either
# one is present, remove it from the required_keys list.
for item in required_keys:

View file

@ -39,6 +39,20 @@ log = logging.getLogger(__name__)
# Version added to novaclient.client.Client function
NOVACLIENT_MINVER = '2.6.1'
# dict for block_device_mapping_v2
CLIENT_BDM2_KEYS = {
'id': 'uuid',
'source': 'source_type',
'dest': 'destination_type',
'bus': 'disk_bus',
'device': 'device_name',
'size': 'volume_size',
'format': 'guest_format',
'bootindex': 'boot_index',
'type': 'device_type',
'shutdown': 'delete_on_termination',
}
def check_nova():
if HAS_NOVA:
@ -56,6 +70,60 @@ class KwargsStruct(object):
self.__dict__.update(entries)
def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None):
bdm = []
if block_device is None:
block_device = []
if ephemeral is None:
ephemeral = []
if boot_volume is not None:
bdm_dict = {'uuid': boot_volume, 'source_type': 'volume',
'destination_type': 'volume', 'boot_index': 0,
'delete_on_termination': False}
bdm.append(bdm_dict)
if snapshot is not None:
bdm_dict = {'uuid': snapshot, 'source_type': 'snapshot',
'destination_type': 'volume', 'boot_index': 0,
'delete_on_termination': False}
bdm.append(bdm_dict)
for device_spec in block_device:
bdm_dict = {}
for key, value in six.iteritems(device_spec):
bdm_dict[CLIENT_BDM2_KEYS[key]] = value
# Convert the delete_on_termination to a boolean or set it to true by
# default for local block devices when not specified.
if 'delete_on_termination' in bdm_dict:
action = bdm_dict['delete_on_termination']
bdm_dict['delete_on_termination'] = (action == 'remove')
elif bdm_dict.get('destination_type') == 'local':
bdm_dict['delete_on_termination'] = True
bdm.append(bdm_dict)
for ephemeral_spec in ephemeral:
bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
'boot_index': -1, 'delete_on_termination': True}
if 'size' in ephemeral_spec:
bdm_dict['volume_size'] = ephemeral_spec['size']
if 'format' in ephemeral_spec:
bdm_dict['guest_format'] = ephemeral_spec['format']
bdm.append(bdm_dict)
if swap is not None:
bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
'boot_index': -1, 'delete_on_termination': True,
'guest_format': 'swap', 'volume_size': swap}
bdm.append(bdm_dict)
return bdm
class NovaServer(object):
def __init__(self, name, server, password=None):
'''
@ -63,7 +131,7 @@ class NovaServer(object):
'''
self.name = name
self.id = server['id']
self.image = server['image']['id']
self.image = server.get('image', {}).get('id', 'Boot From Volume')
self.size = server['flavor']['id']
self.state = server['state']
self._uuid = None
@ -254,12 +322,19 @@ class SaltNova(OpenStackComputeShell):
Boot a cloud server.
'''
nt_ks = self.compute_conn
for key in ('name', 'flavor', 'image'):
if key in kwargs:
del kwargs[key]
response = nt_ks.servers.create(
name=name, flavor=flavor_id, image=image_id, **kwargs
kwargs['name'] = name
kwargs['flavor'] = flavor_id
kwargs['image'] = image_id or None
ephemeral = kwargs.pop('ephemeral', [])
block_device = kwargs.pop('block_device', [])
boot_volume = kwargs.pop('boot_volume', None)
snapshot = kwargs.pop('snapshot', None)
swap = kwargs.pop('swap', None)
kwargs['block_device_mapping_v2'] = _parse_block_device_mapping_v2(
block_device=block_device, boot_volume=boot_volume, snapshot=snapshot,
ephemeral=ephemeral, swap=swap
)
response = nt_ks.servers.create(**kwargs)
self.uuid = response.id
self.password = response.adminPass
@ -694,8 +769,8 @@ class SaltNova(OpenStackComputeShell):
'accessIPv6': item.accessIPv6,
'flavor': {'id': item.flavor['id'],
'links': item.flavor['links']},
'image': {'id': item.image['id'],
'links': item.image['links']},
'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
'links': item.image['links'] if item.image else ''},
}
except TypeError:
pass
@ -720,8 +795,8 @@ class SaltNova(OpenStackComputeShell):
'links': item.flavor['links']},
'hostId': item.hostId,
'id': item.id,
'image': {'id': item.image['id'],
'links': item.image['links']},
'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
'links': item.image['links'] if item.image else ''},
'key_name': item.key_name,
'links': item.links,
'metadata': item.metadata,