mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #29765 from gtmanfred/2015.8
allow nova driver to be boot from volume
This commit is contained in:
commit
1b430b251f
3 changed files with 187 additions and 19 deletions
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue