mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #30508 from rallytime/linode-clone-fixes
Fix Linode driver cloning functionality
This commit is contained in:
commit
15c7aedd46
3 changed files with 114 additions and 63 deletions
|
@ -49,9 +49,9 @@ Set up an initial profile at ``/etc/salt/cloud.profiles`` or in the
|
|||
|
||||
linode_1024:
|
||||
provider: my-linode-config
|
||||
size: Linode 1024
|
||||
image: Arch Linux 2013.06
|
||||
location: london
|
||||
size: Linode 2048
|
||||
image: CentOS 7
|
||||
location: London, England, UK
|
||||
|
||||
Sizes can be obtained using the ``--list-sizes`` option for the ``salt-cloud``
|
||||
command:
|
||||
|
@ -156,13 +156,28 @@ cloud profile that looks like this:
|
|||
li-clone:
|
||||
provider: my-linode-config
|
||||
clonefrom: machine_to_clone
|
||||
script_args: -C
|
||||
script_args: -C -F
|
||||
|
||||
Then run salt-cloud as normal, specifying `-p li-clone`. The profile name can
|
||||
be anything; It doesn't have to be `li-clone`.
|
||||
Then run salt-cloud as normal, specifying ``-p li-clone``. The profile name can
|
||||
be anything; It doesn't have to be ``li-clone``.
|
||||
|
||||
`Clonefrom:` is the name of an existing machine in Linode from which to clone.
|
||||
`Script_args: -C` is necessary to avoid re-deploying Salt via salt-bootstrap.
|
||||
`-C` will just re-deploy keys so the new minion will not have a duplicate key
|
||||
or minion_id on the master.
|
||||
``clonefrom:`` is the name of an existing machine in Linode from which to clone.
|
||||
``Script_args: -C -F`` is necessary to avoid re-deploying Salt via salt-bootstrap.
|
||||
``-C`` will just re-deploy keys so the new minion will not have a duplicate key
|
||||
or minion_id on the Master, and ``-F`` will force a rewrite of the Minion config
|
||||
file on the new Minion. If ``-F`` isn't provided, the new Minion will have the
|
||||
``machine_to_clone``'s Minion ID, instead of its own Minion ID, which can cause
|
||||
problems.
|
||||
|
||||
.. note::
|
||||
|
||||
`Pull Request #733`_ to the salt-bootstrap repo makes the ``-F`` argument
|
||||
non-necessary. Once that change is released into a stable version of the
|
||||
Bootstrap Script, the ``-C`` argument will be sufficient for the ``script_args``
|
||||
setting.
|
||||
|
||||
.. _Pull Request #733: https://github.com/saltstack/salt-bootstrap/pull/733
|
||||
|
||||
If the ``machine_to_clone`` does not have Salt installed on it, refrain from using
|
||||
the ``script_args: -C -F`` altogether, because the new machine will need to have
|
||||
Salt installed.
|
||||
|
|
|
@ -337,6 +337,7 @@ def create(vm_):
|
|||
'''
|
||||
Create a single Linode VM.
|
||||
'''
|
||||
name = vm_['name']
|
||||
try:
|
||||
# Check for required profile parameters before sending any API calls.
|
||||
if vm_['profile'] and config.is_profile_configured(__opts__,
|
||||
|
@ -351,72 +352,95 @@ def create(vm_):
|
|||
if 'provider' in vm_:
|
||||
vm_['driver'] = vm_.pop('provider')
|
||||
|
||||
if _validate_name(vm_['name']) is False:
|
||||
if _validate_name(name) is False:
|
||||
return False
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'starting create',
|
||||
'salt/cloud/{0}/creating'.format(vm_['name']),
|
||||
'salt/cloud/{0}/creating'.format(name),
|
||||
{
|
||||
'name': vm_['name'],
|
||||
'name': name,
|
||||
'profile': vm_['profile'],
|
||||
'provider': vm_['driver'],
|
||||
},
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
log.info('Creating Cloud VM {0}'.format(vm_['name']))
|
||||
log.info('Creating Cloud VM {0}'.format(name))
|
||||
|
||||
data = {}
|
||||
kwargs = {
|
||||
'name': vm_['name'],
|
||||
'image': vm_['image'],
|
||||
'size': vm_['size'],
|
||||
}
|
||||
kwargs = {'name': name}
|
||||
|
||||
plan_id = get_plan_id(kwargs={'label': vm_['size']})
|
||||
try:
|
||||
datacenter_id = get_datacenter_id(vm_['location'])
|
||||
except KeyError:
|
||||
# Linode's default datacenter is Dallas, but we still have to set one to
|
||||
# use the create function from Linode's API. Dallas's datacenter id is 2.
|
||||
datacenter_id = 2
|
||||
plan_id = None
|
||||
size = vm_.get('size')
|
||||
if size:
|
||||
kwargs['size'] = size
|
||||
plan_id = get_plan_id(kwargs={'label': size})
|
||||
|
||||
if 'clonefrom' in vm_:
|
||||
linode_id = get_linode_id_from_name(vm_['clonefrom'])
|
||||
datacenter_id = None
|
||||
location = vm_.get('location')
|
||||
if location:
|
||||
try:
|
||||
datacenter_id = get_datacenter_id(location)
|
||||
except KeyError:
|
||||
# Linode's default datacenter is Dallas, but we still have to set one to
|
||||
# use the create function from Linode's API. Dallas's datacenter id is 2.
|
||||
datacenter_id = 2
|
||||
|
||||
clonefrom_name = vm_.get('clonefrom')
|
||||
cloning = True if clonefrom_name else False
|
||||
if cloning:
|
||||
linode_id = get_linode_id_from_name(clonefrom_name)
|
||||
clone_source = get_linode(kwargs={'linode_id': linode_id})
|
||||
|
||||
kwargs.update({'clonefrom': vm_['clonefrom']})
|
||||
kwargs['image'] = 'Clone of {0}'.format(vm_['clonefrom'])
|
||||
kwargs['size'] = clone_source['TOTALRAM']
|
||||
kwargs = {
|
||||
'clonefrom': clonefrom_name,
|
||||
'image': 'Clone of {0}'.format(clonefrom_name),
|
||||
}
|
||||
|
||||
if size is None:
|
||||
size = clone_source['TOTALRAM']
|
||||
kwargs['size'] = size
|
||||
plan_id = clone_source['PLANID']
|
||||
|
||||
if location is None:
|
||||
datacenter_id = clone_source['DATACENTERID']
|
||||
|
||||
# Create new Linode from cloned Linode
|
||||
try:
|
||||
result = clone(kwargs={'linode_id': linode_id,
|
||||
'datacenter_id': datacenter_id,
|
||||
'plan_id': plan_id})
|
||||
except Exception:
|
||||
except Exception as err:
|
||||
log.error(
|
||||
'Error cloning {0} on Linode\n\n'
|
||||
'Error cloning \'{0}\' on Linode.\n\n'
|
||||
'The following exception was thrown by Linode when trying to '
|
||||
'clone the specified machine:\n'.format(
|
||||
vm_['clonefrom']
|
||||
'clone the specified machine:\n'
|
||||
'{1}'.format(
|
||||
clonefrom_name,
|
||||
err
|
||||
),
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
return False
|
||||
else:
|
||||
kwargs['image'] = vm_['image']
|
||||
|
||||
# Create Linode
|
||||
try:
|
||||
result = _query('linode', 'create', args={
|
||||
'PLANID': plan_id,
|
||||
'DATACENTERID': datacenter_id
|
||||
})
|
||||
except Exception:
|
||||
except Exception as err:
|
||||
log.error(
|
||||
'Error creating {0} on Linode\n\n'
|
||||
'The following exception was thrown by Linode when trying to '
|
||||
'run the initial deployment:\n'.format(
|
||||
vm_['name']
|
||||
'run the initial deployment:\n'
|
||||
'{1}'.format(
|
||||
name,
|
||||
err
|
||||
),
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
|
@ -425,7 +449,7 @@ def create(vm_):
|
|||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'requesting instance',
|
||||
'salt/cloud/{0}/requesting'.format(vm_['name']),
|
||||
'salt/cloud/{0}/requesting'.format(name),
|
||||
{'kwargs': kwargs},
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
@ -436,28 +460,32 @@ def create(vm_):
|
|||
if not _wait_for_status(node_id, status=(_get_status_id_by_name('brand_new'))):
|
||||
log.error(
|
||||
'Error creating {0} on LINODE\n\n'
|
||||
'while waiting for initial ready status'.format(vm_['name']),
|
||||
'while waiting for initial ready status'.format(name),
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
|
||||
# Update the Linode's Label to reflect the given VM name
|
||||
update_linode(node_id, update_args={'Label': vm_['name']})
|
||||
log.debug('Set name for {0} - was linode{1}.'.format(vm_['name'], node_id))
|
||||
|
||||
# Create disks and get ids
|
||||
log.debug('Creating disks for {0}'.format(vm_['name']))
|
||||
root_disk_id = create_disk_from_distro(vm_, node_id)['DiskID']
|
||||
swap_disk_id = create_swap_disk(vm_, node_id)['DiskID']
|
||||
update_linode(node_id, update_args={'Label': name})
|
||||
log.debug('Set name for {0} - was linode{1}.'.format(name, node_id))
|
||||
|
||||
# Add private IP address if requested
|
||||
if get_private_ip(vm_):
|
||||
create_private_ip(vm_, node_id)
|
||||
|
||||
# Create a ConfigID using disk ids
|
||||
config_id = create_config(kwargs={'name': vm_['name'],
|
||||
'linode_id': node_id,
|
||||
'root_disk_id': root_disk_id,
|
||||
'swap_disk_id': swap_disk_id})['ConfigID']
|
||||
if cloning:
|
||||
config_id = get_config_id(kwargs={'linode_id': node_id})['config_id']
|
||||
else:
|
||||
# Create disks and get ids
|
||||
log.debug('Creating disks for {0}'.format(name))
|
||||
root_disk_id = create_disk_from_distro(vm_, node_id)['DiskID']
|
||||
swap_disk_id = create_swap_disk(vm_, node_id)['DiskID']
|
||||
|
||||
# Create a ConfigID using disk ids
|
||||
config_id = create_config(kwargs={'name': name,
|
||||
'linode_id': node_id,
|
||||
'root_disk_id': root_disk_id,
|
||||
'swap_disk_id': swap_disk_id})['ConfigID']
|
||||
|
||||
# Boot the Linode
|
||||
boot(kwargs={'linode_id': node_id,
|
||||
'config_id': config_id,
|
||||
|
@ -467,9 +495,9 @@ def create(vm_):
|
|||
ips = get_ips(node_id)
|
||||
state = int(node_data['STATUS'])
|
||||
|
||||
data['image'] = vm_['image']
|
||||
data['name'] = node_data['LABEL']
|
||||
data['size'] = node_data['TOTALRAM']
|
||||
data['image'] = kwargs['image']
|
||||
data['name'] = name
|
||||
data['size'] = size
|
||||
data['state'] = _get_status_descr_by_id(state)
|
||||
data['private_ips'] = ips['private_ips']
|
||||
data['public_ips'] = ips['public_ips']
|
||||
|
@ -484,19 +512,19 @@ def create(vm_):
|
|||
|
||||
ret.update(data)
|
||||
|
||||
log.info('Created Cloud VM {0[name]!r}'.format(vm_))
|
||||
log.info('Created Cloud VM {0!r}'.format(name))
|
||||
log.debug(
|
||||
'{0[name]!r} VM creation details:\n{1}'.format(
|
||||
vm_, pprint.pformat(data)
|
||||
'{0!r} VM creation details:\n{1}'.format(
|
||||
name, pprint.pformat(data)
|
||||
)
|
||||
)
|
||||
|
||||
salt.utils.cloud.fire_event(
|
||||
'event',
|
||||
'created instance',
|
||||
'salt/cloud/{0}/created'.format(vm_['name']),
|
||||
'salt/cloud/{0}/created'.format(name),
|
||||
{
|
||||
'name': vm_['name'],
|
||||
'name': name,
|
||||
'profile': vm_['profile'],
|
||||
'provider': vm_['driver'],
|
||||
},
|
||||
|
@ -781,8 +809,8 @@ def get_distribution_id(vm_):
|
|||
|
||||
if not distro_id:
|
||||
raise SaltCloudNotFound(
|
||||
'The DistributionID for the {0} profile could not be found.\n'
|
||||
'The {1} instance could not be provisioned.'.format(
|
||||
'The DistributionID for the \'{0}\' profile could not be found.\n'
|
||||
'The \'{1}\' instance could not be provisioned.'.format(
|
||||
vm_image_name,
|
||||
vm_['name']
|
||||
)
|
||||
|
|
|
@ -2606,9 +2606,17 @@ def is_profile_configured(opts, provider, profile_name):
|
|||
provider_key = opts['providers'][alias][driver]
|
||||
profile_key = opts['providers'][alias][driver]['profiles'][profile_name]
|
||||
|
||||
# If cloning on Linode, size and image are not necessary.
|
||||
# They are obtained from the to-be-cloned VM.
|
||||
linode_cloning = False
|
||||
if driver == 'linode' and profile_key.get('clonefrom'):
|
||||
linode_cloning = True
|
||||
non_image_drivers.append('linode')
|
||||
non_size_drivers.append('linode')
|
||||
|
||||
if driver not in non_image_drivers:
|
||||
required_keys.append('image')
|
||||
elif driver == 'vmware':
|
||||
elif driver == 'vmware' or linode_cloning:
|
||||
required_keys.append('clonefrom')
|
||||
elif driver == 'nova':
|
||||
nova_image_keys = ['image', 'block_device_mapping', 'block_device']
|
||||
|
|
Loading…
Add table
Reference in a new issue