%PDF- %PDF-
Direktori : /proc/self/root/lib/python2.7/site-packages/salt/cloud/clouds/ |
Current File : //proc/self/root/lib/python2.7/site-packages/salt/cloud/clouds/parallels.py |
# -*- coding: utf-8 -*- ''' Parallels Cloud Module ====================== The Parallels cloud module is used to control access to cloud providers using the Parallels VPS system. Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/parallels.conf``: .. code-block:: yaml my-parallels-config: # Parallels account information user: myuser password: mypassword url: https://api.cloud.xmission.com:4465/paci/v1.0/ driver: parallels ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals import time import pprint import logging # Import Salt libs from salt._compat import ElementTree as ET # pylint: disable=import-error,no-name-in-module from salt.ext.six.moves.urllib.error import URLError from salt.ext.six.moves.urllib.parse import urlencode as _urlencode from salt.ext.six.moves.urllib.request import ( HTTPBasicAuthHandler as _HTTPBasicAuthHandler, Request as _Request, urlopen as _urlopen, build_opener as _build_opener, install_opener as _install_opener ) # pylint: enable=import-error,no-name-in-module # Import salt cloud libs import salt.utils.cloud import salt.config as config from salt.exceptions import ( SaltCloudNotFound, SaltCloudSystemExit, SaltCloudExecutionFailure, SaltCloudExecutionTimeout ) # Import 3rd-party libs from salt.ext import six # Get logging started log = logging.getLogger(__name__) __virtualname__ = 'parallels' # Only load in this module if the PARALLELS configurations are in place def __virtual__(): ''' Check for PARALLELS configurations ''' if get_configured_provider() is False: return False return __virtualname__ def get_configured_provider(): ''' Return the first configured instance. ''' return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, ('user', 'password', 'url',) ) def avail_images(call=None): ''' Return a list of the images that are on the provider ''' if call == 'action': raise SaltCloudSystemExit( 'The avail_images function must be called with ' '-f or --function, or with the --list-images option' ) items = query(action='template') ret = {} for item in items: ret[item.attrib['name']] = item.attrib return ret def list_nodes(call=None): ''' Return a list of the VMs that are on the provider ''' if call == 'action': raise SaltCloudSystemExit( 'The list_nodes function must be called with -f or --function.' ) ret = {} items = query(action='ve') for item in items: name = item.attrib['name'] node = show_instance(name, call='action') ret[name] = { 'id': node['id'], 'image': node['platform']['template-info']['name'], 'state': node['state'], } if 'private-ip' in node['network']: ret[name]['private_ips'] = [node['network']['private-ip']] if 'public-ip' in node['network']: ret[name]['public_ips'] = [node['network']['public-ip']] return ret def list_nodes_full(call=None): ''' Return a list of the VMs that are on the provider ''' if call == 'action': raise SaltCloudSystemExit( 'The list_nodes_full function must be called with -f or --function.' ) ret = {} items = query(action='ve') for item in items: name = item.attrib['name'] node = show_instance(name, call='action') ret[name] = node ret[name]['image'] = node['platform']['template-info']['name'] if 'private-ip' in node['network']: ret[name]['private_ips'] = [ node['network']['private-ip']['address'] ] if 'public-ip' in node['network']: ret[name]['public_ips'] = [ node['network']['public-ip']['address'] ] return ret def list_nodes_select(call=None): ''' Return a list of the VMs that are on the provider, with select fields ''' return salt.utils.cloud.list_nodes_select( list_nodes_full(), __opts__['query.selection'], call, ) def get_image(vm_): ''' Return the image object to use ''' images = avail_images() vm_image = config.get_cloud_config_value( 'image', vm_, __opts__, search_global=False ) for image in images: if six.text_type(vm_image) in (images[image]['name'], images[image]['id']): return images[image]['id'] raise SaltCloudNotFound('The specified image could not be found.') def create_node(vm_): ''' Build and submit the XML to create a node ''' # Start the tree content = ET.Element('ve') # Name of the instance name = ET.SubElement(content, 'name') name.text = vm_['name'] # Description, defaults to name desc = ET.SubElement(content, 'description') desc.text = config.get_cloud_config_value( 'desc', vm_, __opts__, default=vm_['name'], search_global=False ) # How many CPU cores, and how fast they are cpu = ET.SubElement(content, 'cpu') cpu.attrib['number'] = config.get_cloud_config_value( 'cpu_number', vm_, __opts__, default='1', search_global=False ) cpu.attrib['power'] = config.get_cloud_config_value( 'cpu_power', vm_, __opts__, default='1000', search_global=False ) # How many megabytes of RAM ram = ET.SubElement(content, 'ram-size') ram.text = config.get_cloud_config_value( 'ram', vm_, __opts__, default='256', search_global=False ) # Bandwidth available, in kbps bandwidth = ET.SubElement(content, 'bandwidth') bandwidth.text = config.get_cloud_config_value( 'bandwidth', vm_, __opts__, default='100', search_global=False ) # How many public IPs will be assigned to this instance ip_num = ET.SubElement(content, 'no-of-public-ip') ip_num.text = config.get_cloud_config_value( 'ip_num', vm_, __opts__, default='1', search_global=False ) # Size of the instance disk disk = ET.SubElement(content, 've-disk') disk.attrib['local'] = 'true' disk.attrib['size'] = config.get_cloud_config_value( 'disk_size', vm_, __opts__, default='10', search_global=False ) # Attributes for the image vm_image = config.get_cloud_config_value( 'image', vm_, __opts__, search_global=False ) image = show_image({'image': vm_image}, call='function') platform = ET.SubElement(content, 'platform') template = ET.SubElement(platform, 'template-info') template.attrib['name'] = vm_image os_info = ET.SubElement(platform, 'os-info') os_info.attrib['technology'] = image[vm_image]['technology'] os_info.attrib['type'] = image[vm_image]['osType'] # Username and password admin = ET.SubElement(content, 'admin') admin.attrib['login'] = config.get_cloud_config_value( 'ssh_username', vm_, __opts__, default='root' ) admin.attrib['password'] = config.get_cloud_config_value( 'password', vm_, __opts__, search_global=False ) data = ET.tostring(content, encoding='UTF-8') __utils__['cloud.fire_event']( 'event', 'requesting instance', 'salt/cloud/{0}/requesting'.format(vm_['name']), args={ 'kwargs': __utils__['cloud.filter_event']('requesting', data, list(data)), }, sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) node = query(action='ve', method='POST', data=data) return node def create(vm_): ''' Create a single VM from a data dict ''' try: # Check for required profile parameters before sending any API calls. if vm_['profile'] and config.is_profile_configured(__opts__, __active_provider_name__ or 'parallels', vm_['profile'], vm_=vm_) is False: return False except AttributeError: pass __utils__['cloud.fire_event']( 'event', 'starting create', 'salt/cloud/{0}/creating'.format(vm_['name']), args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) log.info('Creating Cloud VM %s', vm_['name']) try: data = create_node(vm_) except Exception as exc: log.error( 'Error creating %s on PARALLELS\n\n' 'The following exception was thrown when trying to ' 'run the initial deployment: \n%s', vm_['name'], exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG ) return False name = vm_['name'] if not wait_until(name, 'CREATED'): return {'Error': 'Unable to start {0}, command timed out'.format(name)} start(vm_['name'], call='action') if not wait_until(name, 'STARTED'): return {'Error': 'Unable to start {0}, command timed out'.format(name)} def __query_node_data(vm_name): data = show_instance(vm_name, call='action') if 'public-ip' not in data['network']: # Trigger another iteration return return data try: data = salt.utils.cloud.wait_for_ip( __query_node_data, update_args=(vm_['name'],), timeout=config.get_cloud_config_value( 'wait_for_ip_timeout', vm_, __opts__, default=5 * 60), interval=config.get_cloud_config_value( 'wait_for_ip_interval', vm_, __opts__, default=5), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! destroy(vm_['name']) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) comps = data['network']['public-ip']['address'].split('/') public_ip = comps[0] vm_['ssh_host'] = public_ip ret = __utils__['cloud.bootstrap'](vm_, __opts__) log.info('Created Cloud VM \'%s\'', vm_['name']) log.debug( '\'%s\' VM creation details:\n%s', vm_['name'], pprint.pformat(data) ) __utils__['cloud.fire_event']( 'event', 'created instance', 'salt/cloud/{0}/created'.format(vm_['name']), args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) return data def query(action=None, command=None, args=None, method='GET', data=None): ''' Make a web call to a Parallels provider ''' path = config.get_cloud_config_value( 'url', get_configured_provider(), __opts__, search_global=False ) auth_handler = _HTTPBasicAuthHandler() auth_handler.add_password( realm='Parallels Instance Manager', uri=path, user=config.get_cloud_config_value( 'user', get_configured_provider(), __opts__, search_global=False ), passwd=config.get_cloud_config_value( 'password', get_configured_provider(), __opts__, search_global=False ) ) opener = _build_opener(auth_handler) _install_opener(opener) if action: path += action if command: path += '/{0}'.format(command) if not type(args, dict): args = {} kwargs = {'data': data} if isinstance(data, six.string_types) and '<?xml' in data: kwargs['headers'] = { 'Content-type': 'application/xml', } if args: params = _urlencode(args) req = _Request(url='{0}?{1}'.format(path, params), **kwargs) else: req = _Request(url=path, **kwargs) req.get_method = lambda: method log.debug('%s %s', method, req.get_full_url()) if data: log.debug(data) try: result = _urlopen(req) log.debug('PARALLELS Response Status Code: %s', result.getcode()) if 'content-length' in result.headers: content = result.read() result.close() items = ET.fromstring(content) return items return {} except URLError as exc: log.error('PARALLELS Response Status Code: %s %s', exc.code, exc.msg) root = ET.fromstring(exc.read()) log.error(root) return {'error': root} def script(vm_): ''' Return the script deployment object ''' return salt.utils.cloud.os_script( config.get_cloud_config_value('script', vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) ) ) def show_image(kwargs, call=None): ''' Show the details from Parallels concerning an image ''' if call != 'function': raise SaltCloudSystemExit( 'The show_image function must be called with -f or --function.' ) items = query(action='template', command=kwargs['image']) if 'error' in items: return items['error'] ret = {} for item in items: ret.update({item.attrib['name']: item.attrib}) return ret def show_instance(name, call=None): ''' Show the details from Parallels concerning an instance ''' if call != 'action': raise SaltCloudSystemExit( 'The show_instance action must be called with -a or --action.' ) items = query(action='ve', command=name) ret = {} for item in items: if 'text' in item.__dict__: ret[item.tag] = item.text else: ret[item.tag] = item.attrib if item._children: ret[item.tag] = {} children = item._children for child in children: ret[item.tag][child.tag] = child.attrib __utils__['cloud.cache_node'](ret, __active_provider_name__, __opts__) return ret def wait_until(name, state, timeout=300): ''' Wait until a specific state has been reached on a node ''' start_time = time.time() node = show_instance(name, call='action') while True: if node['state'] == state: return True time.sleep(1) if time.time() - start_time > timeout: return False node = show_instance(name, call='action') def destroy(name, call=None): ''' Destroy a node. CLI Example: .. code-block:: bash salt-cloud --destroy mymachine ''' if call == 'function': raise SaltCloudSystemExit( 'The destroy action must be called with -d, --destroy, ' '-a or --action.' ) __utils__['cloud.fire_event']( 'event', 'destroying instance', 'salt/cloud/{0}/destroying'.format(name), args={'name': name}, sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) node = show_instance(name, call='action') if node['state'] == 'STARTED': stop(name, call='action') if not wait_until(name, 'STOPPED'): return { 'Error': 'Unable to destroy {0}, command timed out'.format( name ) } data = query(action='ve', command=name, method='DELETE') if 'error' in data: return data['error'] __utils__['cloud.fire_event']( 'event', 'destroyed instance', 'salt/cloud/{0}/destroyed'.format(name), args={'name': name}, sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) if __opts__.get('update_cachedir', False) is True: __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) return {'Destroyed': '{0} was destroyed.'.format(name)} def start(name, call=None): ''' Start a node. CLI Example: .. code-block:: bash salt-cloud -a start mymachine ''' if call != 'action': raise SaltCloudSystemExit( 'The show_instance action must be called with -a or --action.' ) data = query(action='ve', command='{0}/start'.format(name), method='PUT') if 'error' in data: return data['error'] return {'Started': '{0} was started.'.format(name)} def stop(name, call=None): ''' Stop a node. CLI Example: .. code-block:: bash salt-cloud -a stop mymachine ''' if call != 'action': raise SaltCloudSystemExit( 'The show_instance action must be called with -a or --action.' ) data = query(action='ve', command='{0}/stop'.format(name), method='PUT') if 'error' in data: return data['error'] return {'Stopped': '{0} was stopped.'.format(name)}