%PDF- %PDF-
Direktori : /proc/thread-self/root/usr/lib/python2.7/site-packages/salt/cloud/clouds/ |
Current File : //proc/thread-self/root/usr/lib/python2.7/site-packages/salt/cloud/clouds/packet.py |
# -*- coding: utf-8 -*- ''' Packet Cloud Module Using Packet's Python API Client =========================================== The Packet cloud module is used to control access to the Packet VPS system. Use of this module only requires the ``token`` parameter. Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/packet.conf``: The Packet profile requires ``size``, ``image``, ``location``, ``project_id`` Optional profile parameters: - ``storage_size`` - min value is 10, defines Gigabytes of storage that will be attached to device. - ``storage_tier`` - storage_1 - Standard Plan, storage_2 - Performance Plan - ``snapshot_count`` - int - ``snapshot_frequency`` - string - possible values: - 1min - 15min - 1hour - 1day - 1week - 1month - 1year This driver requires Packet's client library: https://pypi.python.org/pypi/packet-python .. code-block:: yaml packet-provider: minion: master: 192.168.50.10 driver: packet token: ewr23rdf35wC8oNjJrhmHa87rjSXzJyi private_key: /root/.ssh/id_rsa packet-profile: provider: packet-provider size: baremetal_0 image: ubuntu_16_04_image location: ewr1 project_id: a64d000b-d47c-4d26-9870-46aac43010a6 storage_size: 10 storage_tier: storage_1 storage_snapshot_count: 1 storage_snapshot_frequency: 15min ''' # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals import logging import pprint import time # Import 3rd-party libs try: import packet HAS_PACKET = True except ImportError: HAS_PACKET = False # Import Salt Libs import salt.config as config from salt.ext.six.moves import range from salt.exceptions import ( SaltCloudException, SaltCloudSystemExit ) # Import Salt-Cloud Libs import salt.utils.cloud from salt.cloud.libcloudfuncs import get_size, get_image, script, show_instance from salt.utils.functools import namespaced_function get_size = namespaced_function(get_size, globals()) get_image = namespaced_function(get_image, globals()) script = namespaced_function(script, globals()) show_instance = namespaced_function(show_instance, globals()) # Get logging started log = logging.getLogger(__name__) __virtualname__ = 'packet' # Only load this module if the Packet configuration is in place. def __virtual__(): ''' Check for Packet configs. ''' if HAS_PACKET is False: return False, 'The packet python library is not installed' 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__, ('token',) ) def avail_images(call=None): ''' Return available Packet os images. CLI Example: .. code-block:: bash salt-cloud --list-images packet-provider salt-cloud -f avail_images packet-provider ''' if call == 'action': raise SaltCloudException( 'The avail_images function must be called with -f or --function.' ) ret = {} vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) ret = {} for os_system in manager.list_operating_systems(): ret[os_system.name] = os_system.__dict__ return ret def avail_locations(call=None): ''' Return available Packet datacenter locations. CLI Example: .. code-block:: bash salt-cloud --list-locations packet-provider salt-cloud -f avail_locations packet-provider ''' if call == 'action': raise SaltCloudException( 'The avail_locations function must be called with -f or --function.' ) vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) ret = {} for facility in manager.list_facilities(): ret[facility.name] = facility.__dict__ return ret def avail_sizes(call=None): ''' Return available Packet sizes. CLI Example: .. code-block:: bash salt-cloud --list-sizes packet-provider salt-cloud -f avail_sizes packet-provider ''' if call == 'action': raise SaltCloudException( 'The avail_locations function must be called with -f or --function.' ) vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) ret = {} for plan in manager.list_plans(): ret[plan.name] = plan.__dict__ return ret def avail_projects(call=None): ''' Return available Packet projects. CLI Example: .. code-block:: bash salt-cloud -f avail_projects packet-provider ''' if call == 'action': raise SaltCloudException( 'The avail_projects function must be called with -f or --function.' ) vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) ret = {} for project in manager.list_projects(): ret[project.name] = project.__dict__ return ret def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=True): ''' Wait for a certain status from Packet. status_type device or volume object_id The ID of the Packet device or volume to wait on. Required. status The status to wait for. timeout The amount of time to wait for a status to update. quiet Log status updates to debug logs when False. Otherwise, logs to info. ''' if status is None: status = "ok" interval = 5 iterations = int(timeout / interval) vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) for i in range(0, iterations): get_object = getattr(manager, "get_{status_type}".format(status_type=status_type)) obj = get_object(object_id) if obj.state == status: return obj time.sleep(interval) log.log( logging.INFO if not quiet else logging.DEBUG, 'Status for Packet %s is \'%s\', waiting for \'%s\'.', object_id, obj.state, status ) return obj def is_profile_configured(vm_): try: # Check for required profile parameters before sending any API calls. if vm_['profile'] and config.is_profile_configured(__opts__, __active_provider_name__ or __virtualname__, vm_['profile'], vm_=vm_) is False: return False alias, driver = __active_provider_name__.split(':') profile_data = __opts__['providers'][alias][driver]['profiles'][vm_['profile']] if profile_data.get('storage_size') or profile_data.get('storage_tier'): required_keys = ['storage_size', 'storage_tier'] for key in required_keys: if profile_data.get(key) is None: log.error( 'both storage_size and storage_tier required for ' 'profile %s. Please check your profile configuration', vm_['profile'] ) return False locations = avail_locations() for location in locations.values(): if location['code'] == profile_data['location']: if 'storage' not in location['features']: log.error( 'Chosen location %s for profile %s does not ' 'support storage feature. Please check your ' 'profile configuration', location['code'], vm_['profile'] ) return False if profile_data.get('storage_snapshot_count') or profile_data.get('storage_snapshot_frequency'): required_keys = ['storage_size', 'storage_tier'] for key in required_keys: if profile_data.get(key) is None: log.error( 'both storage_snapshot_count and ' 'storage_snapshot_frequency required for profile ' '%s. Please check your profile configuration', vm_['profile'] ) return False except AttributeError: pass return True def create(vm_): ''' Create a single Packet VM. ''' name = vm_['name'] if not is_profile_configured(vm_): return False __utils__['cloud.fire_event']( 'event', 'starting create', 'salt/cloud/{0}/creating'.format(name), args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) log.info('Creating Packet VM %s', name) manager = packet.Manager(auth_token=vm_['token']) __utils__['cloud.fire_event']( 'event', 'requesting instance', 'salt/cloud/{0}/requesting'.format(vm_['name']), args=__utils__['cloud.filter_event']('requesting', vm_, ['name', 'profile', 'provider', 'driver']), sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) device = manager.create_device(project_id=vm_['project_id'], hostname=name, plan=vm_['size'], facility=vm_['location'], operating_system=vm_['image']) device = _wait_for_status('device', device.id, status="active") if device.state != "active": log.error( 'Error creating %s on PACKET\n\n' 'while waiting for initial ready status', name, exc_info_on_loglevel=logging.DEBUG ) # Define which ssh_interface to use ssh_interface = _get_ssh_interface(vm_) # Pass the correct IP address to the bootstrap ssh_host key if ssh_interface == 'private_ips': for ip in device.ip_addresses: if ip['public'] is False: vm_['ssh_host'] = ip['address'] break else: for ip in device.ip_addresses: if ip['public'] is True: vm_['ssh_host'] = ip['address'] break key_filename = config.get_cloud_config_value( 'private_key', vm_, __opts__, search_global=False, default=None ) vm_['key_filename'] = key_filename vm_['private_key'] = key_filename # Bootstrap! ret = __utils__['cloud.bootstrap'](vm_, __opts__) ret.update({'device': device.__dict__}) if vm_.get('storage_tier') and vm_.get('storage_size'): # create storage and attach it to device volume = manager.create_volume( vm_['project_id'], "{0}_storage".format(name), vm_.get('storage_tier'), vm_.get('storage_size'), vm_.get('location'), snapshot_count=vm_.get('storage_snapshot_count', 0), snapshot_frequency=vm_.get('storage_snapshot_frequency')) volume.attach(device.id) volume = _wait_for_status('volume', volume.id, status="active") if volume.state != "active": log.error( 'Error creating %s on PACKET\n\n' 'while waiting for initial ready status', name, exc_info_on_loglevel=logging.DEBUG ) ret.update({'volume': volume.__dict__}) log.info('Created Cloud VM \'%s\'', name) log.debug( '\'%s\' VM creation details:\n%s', name, pprint.pformat(device.__dict__) ) __utils__['cloud.fire_event']( 'event', 'created instance', 'salt/cloud/{0}/created'.format(name), args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) return ret def list_nodes_full(call=None): ''' List devices, with all available information. CLI Example: .. code-block:: bash salt-cloud -F salt-cloud --full-query salt-cloud -f list_nodes_full packet-provider .. ''' if call == 'action': raise SaltCloudException( 'The list_nodes_full function must be called with -f or --function.' ) ret = {} for device in get_devices_by_token(): ret[device.hostname] = device.__dict__ return ret def list_nodes_min(call=None): ''' Return a list of the VMs that are on the provider. Only a list of VM names and their state is returned. This is the minimum amount of information needed to check for existing VMs. .. versionadded:: 2015.8.0 CLI Example: .. code-block:: bash salt-cloud -f list_nodes_min packet-provider salt-cloud --function list_nodes_min packet-provider ''' if call == 'action': raise SaltCloudSystemExit( 'The list_nodes_min function must be called with -f or --function.' ) ret = {} for device in get_devices_by_token(): ret[device.hostname] = {'id': device.id, 'state': device.state} 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_devices_by_token(): vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) devices = [] for profile_name in vm_['profiles']: profile = vm_['profiles'][profile_name] devices.extend(manager.list_devices(profile['project_id'])) return devices def list_nodes(call=None): ''' Returns a list of devices, keeping only a brief listing. CLI Example: .. code-block:: bash salt-cloud -Q salt-cloud --query salt-cloud -f list_nodes packet-provider .. ''' if call == 'action': raise SaltCloudException( 'The list_nodes function must be called with -f or --function.' ) ret = {} for device in get_devices_by_token(): ret[device.hostname] = device.__dict__ return ret def destroy(name, call=None): ''' Destroys a Packet device by name. name The hostname of VM to be be destroyed. CLI Example: .. code-block:: bash salt-cloud -d name ''' if call == 'function': raise SaltCloudException( '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'] ) vm_ = get_configured_provider() manager = packet.Manager(auth_token=vm_['token']) nodes = list_nodes_min() node = nodes[name] for project in manager.list_projects(): for volume in manager.list_volumes(project.id): if volume.attached_to == node['id']: volume.detach() volume.delete() break manager.call_api("devices/{id}".format(id=node['id']), type='DELETE') __utils__['cloud.fire_event']( 'event', 'destroyed instance', 'salt/cloud/{0}/destroyed'.format(name), args={'name': name}, sock_dir=__opts__['sock_dir'], transport=__opts__['transport'] ) return {} def _get_ssh_interface(vm_): ''' Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. ''' return config.get_cloud_config_value( 'ssh_interface', vm_, __opts__, default='public_ips', search_global=False )