Source code for SoftLayer.managers.cci

"""
    SoftLayer.cci
    ~~~~~~~~~~~~~
    CCI Manager/helpers

    :copyright: (c) 2013, SoftLayer Technologies, Inc. All rights reserved.
    :license: MIT, see LICENSE for more details.
"""
import socket
from time import sleep
from itertools import repeat

from SoftLayer.utils import NestedDict, query_filter, IdentifierMixin


[docs]class CCIManager(IdentifierMixin, object): """ Manage CCIs """ def __init__(self, client): #: A valid `SoftLayer.API.Client` object that will be used for all #: actions. self.client = client #: Reference to the SoftLayer_Account API object. self.account = client['Account'] #: Reference to the SoftLayer_Virtual_Guest API object. self.guest = client['Virtual_Guest'] #: A list of resolver functions. Used primarily by the CLI to provide #: a variety of methods for uniquely identifying an object such as #: hostname and IP address. self.resolvers = [self._get_ids_from_ip, self._get_ids_from_hostname]
[docs] def list_instances(self, hourly=True, monthly=True, tags=None, cpus=None, memory=None, hostname=None, domain=None, local_disk=None, datacenter=None, nic_speed=None, public_ip=None, private_ip=None, **kwargs): """ Retrieve a list of all CCIs on the account. :param boolean hourly: include hourly instances :param boolean monthly: include monthly instances :param list tags: filter based on tags :param integer cpus: filter based on number of CPUS :param integer memory: filter based on amount of memory :param string hostname: filter based on hostname :param string domain: filter based on domain :param string local_disk: filter based on local_disk :param string datacenter: filter based on datacenter :param integer nic_speed: filter based on network speed (in MBPS) :param string public_ip: filter based on public ip address :param string private_ip: filter based on private ip address :param dict \*\*kwargs: response-level arguments (limit, offset, etc.) :returns: Returns a list of dictionaries representing the matching CCIs :: # Print out a list of all hourly CCIs in the DAL05 data center. # env variables # SL_USERNAME = YOUR_USERNAME # SL_API_KEY = YOUR_API_KEY import SoftLayer client = SoftLayer.Client() mgr = SoftLayer.CCIManager(client) for cci in mgr.list_instances(hourly=True, datacenter='dal05'): print cci['fullyQualifiedDomainName'], cci['primaryIpAddress'] """ if 'mask' not in kwargs: items = set([ 'id', 'globalIdentifier', 'hostname', 'domain', 'fullyQualifiedDomainName', 'primaryBackendIpAddress', 'primaryIpAddress', 'lastKnownPowerState.name', 'powerState', 'maxCpu', 'maxMemory', 'datacenter', 'activeTransaction.transactionStatus[friendlyName,name]', 'status', ]) kwargs['mask'] = "mask[%s]" % ','.join(items) call = 'getVirtualGuests' if not all([hourly, monthly]): if hourly: call = 'getHourlyVirtualGuests' elif monthly: call = 'getMonthlyVirtualGuests' _filter = NestedDict(kwargs.get('filter') or {}) if tags: _filter['virtualGuests']['tagReferences']['tag']['name'] = { 'operation': 'in', 'options': [{'name': 'data', 'value': tags}], } if cpus: _filter['virtualGuests']['maxCpu'] = query_filter(cpus) if memory: _filter['virtualGuests']['maxMemory'] = query_filter(memory) if hostname: _filter['virtualGuests']['hostname'] = query_filter(hostname) if domain: _filter['virtualGuests']['domain'] = query_filter(domain) if local_disk is not None: _filter['virtualGuests']['localDiskFlag'] = \ query_filter(bool(local_disk)) if datacenter: _filter['virtualGuests']['datacenter']['name'] = \ query_filter(datacenter) if nic_speed: _filter['virtualGuests']['networkComponents']['maxSpeed'] = \ query_filter(nic_speed) if public_ip: _filter['virtualGuests']['primaryIpAddress'] = \ query_filter(public_ip) if private_ip: _filter['virtualGuests']['primaryBackendIpAddress'] = \ query_filter(private_ip) kwargs['filter'] = _filter.to_dict() func = getattr(self.account, call) return func(**kwargs)
[docs] def get_instance(self, id, **kwargs): """ Get details about a CCI instance :param integer id: the instance ID :returns: A dictionary containing a large amount of information about the specified instance. """ if 'mask' not in kwargs: items = set([ 'id', 'globalIdentifier', 'fullyQualifiedDomainName', 'hostname', 'domain', 'createDate', 'modifyDate', 'provisionDate', 'notes', 'dedicatedAccountHostOnlyFlag', 'privateNetworkOnlyFlag', 'primaryBackendIpAddress', 'primaryIpAddress', 'networkComponents[id, status, speed, maxSpeed, name,' 'macAddress, primaryIpAddress, port, primarySubnet]', 'lastKnownPowerState.name', 'powerState', 'status', 'maxCpu', 'maxMemory', 'datacenter', 'activeTransaction[id, transactionStatus[friendlyName,name]]', 'blockDevices', 'blockDeviceTemplateGroup[id, name, globalIdentifier]', 'postInstallScriptUri', 'userData', 'operatingSystem.softwareLicense.' 'softwareDescription[manufacturer,name,version,referenceCode]', 'operatingSystem.passwords[username,password]', 'hourlyBillingFlag', 'billingItem.recurringFee', 'tagReferences[id,tag[name,id]]', 'networkVlans[id,vlanNumber,networkSpace]', ]) kwargs['mask'] = "mask[%s]" % ','.join(items) return self.guest.getObject(id=id, **kwargs)
[docs] def get_create_options(self): """ Retrieves the available options for creating a CCI. :returns: A dictionary of creation options. """ return self.guest.getCreateObjectOptions()
[docs] def cancel_instance(self, id): """ Cancel an instance immediately, deleting all its data. :param integer id: the instance ID to cancel """ return self.guest.deleteObject(id=id)
[docs] def reload_instance(self, id, post_uri=None, ssh_keys=None): """ Perform an OS reload of an instance with its current configuration. :param integer id: the instance ID to reload :param string post_url: The URI of the post-install script to run after reload :param list ssh_keys: The SSH keys to add to the root user """ payload = { 'token': 'FORCE', 'config': {}, } if post_uri: payload['config']['customProvisionScriptUri'] = post_uri if ssh_keys: payload['config']['sshKeyIds'] = [key_id for key_id in ssh_keys] return self.guest.reloadOperatingSystem('FORCE', payload['config'], id=id)
def _generate_create_dict( self, cpus=None, memory=None, hourly=True, hostname=None, domain=None, local_disk=True, datacenter=None, os_code=None, image_id=None, dedicated=False, public_vlan=None, private_vlan=None, userdata=None, nic_speed=None, disks=None, post_uri=None, private=False, ssh_keys=None): """ Translates a list of arguments into a dictionary necessary for creating a CCI. :param int cpus: The number of virtual CPUs to include in the instance. :param int memory: The amount of RAM to order. :param bool hourly: Flag to indicate if this server should be billed hourly (default) or monthly. :param string hostname: The hostname to use for the new server. :param string domain: The domain to use for the new server. :param bool local_disk: Flag to indicate if this should be a local disk (default) or a SAN disk. :param string datacenter: The short name of the data center in which the CCI should reside. :param string os_code: The operating system to use. Cannot be specified if image_id is specified. :param int image_id: The ID of the image to load onto the server. Cannot be specified if os_code is specified. :param bool dedicated: Flag to indicate if this should be housed on a dedicated or shared host (default). This will incur a fee on your account. :param int public_vlan: The ID of the public VLAN on which you want this CCI placed. :param int private_vlan: The ID of the public VLAN on which you want this CCI placed. :param bool bare_metal: Flag to indicate if this is a bare metal server or a dedicated server (default). :param list disks: A list of disk capacities for this server. :param string post_url: The URI of the post-install script to run after reload :param bool private: If true, the CCI will be provisioned only with access to the private network. Defaults to false :param list ssh_keys: The SSH keys to add to the root user """ required = [cpus, memory, hostname, domain] mutually_exclusive = [ {'os_code': os_code, "image_id": image_id}, ] if not all(required): raise ValueError("cpu, memory, hostname, and domain are required") for me in mutually_exclusive: if all(me.values()): raise ValueError( 'Can only specify one of: %s' % (','.join(me.keys()))) data = { "startCpus": int(cpus), "maxMemory": int(memory), "hostname": hostname, "domain": domain, "localDiskFlag": local_disk, } data["hourlyBillingFlag"] = hourly if dedicated: data["dedicatedAccountHostOnlyFlag"] = dedicated if private: data['privateNetworkOnlyFlag'] = private if image_id: data["blockDeviceTemplateGroup"] = {"globalIdentifier": image_id} elif os_code: data["operatingSystemReferenceCode"] = os_code if datacenter: data["datacenter"] = {"name": datacenter} if public_vlan: data.update({ 'primaryNetworkComponent': { "networkVlan": {"id": int(public_vlan)}}}) if private_vlan: data.update({ "primaryBackendNetworkComponent": { "networkVlan": {"id": int(private_vlan)}}}) if userdata: data['userData'] = [{'value': userdata}] if nic_speed: data['networkComponents'] = [{'maxSpeed': nic_speed}] if isinstance(disks, list): data['blockDevices'] = [ {"device": "0", "diskImage": {"capacity": disks[0]}} ] # disk 1 is reservered for swap # XXX: enumerate(iterator, start=0) was added in 2.6. work around # for 2.5 by adding 2 to the enumerated value for dev_id, disk in enumerate(disks[1:]): data['blockDevices'].append( { "device": str(dev_id + 2), "diskImage": {"capacity": disk} } ) if post_uri: data['postInstallScriptUri'] = post_uri if ssh_keys: data['sshKeys'] = [{'id': key_id} for key_id in ssh_keys] return data
[docs] def wait_for_transaction(self, id, limit, delay=1): """ Waits on a CCI transaction for the specified amount of time. :param int id: The instance ID with the pending transaction :param int limit: The maximum amount of time to wait. :param int delay: The number of seconds to sleep before checks. Defaults to 1. """ for count, new_instance in enumerate(repeat(id)): instance = self.get_instance(new_instance) if not instance.get('activeTransaction', {}).get('id') and \ instance.get('provisionDate'): return True if count >= limit: return False sleep(delay)
[docs] def verify_create_instance(self, **kwargs): """ Verifies an instance creation command without actually placing an order. See :func:`_generate_create_dict` for a list of available options. """ create_options = self._generate_create_dict(**kwargs) return self.guest.generateOrderTemplate(create_options)
[docs] def create_instance(self, **kwargs): """ Orders a new instance. See :func:`_generate_create_dict` for a list of available options. """ create_options = self._generate_create_dict(**kwargs) return self.guest.createObject(create_options)
[docs] def change_port_speed(self, id, public, speed): """ Allows you to change the port speed of a CCI's NICs. :param int id: The ID of the CCI :param bool public: Flag to indicate which interface to change. True (default) means the public interface. False indicates the private interface. :param int speed: The port speed to set. """ if public: func = self.guest.setPublicNetworkInterfaceSpeed else: func = self.guest.setPrivateNetworkInterfaceSpeed return func(speed, id=id)
def _get_ids_from_hostname(self, hostname): results = self.list_instances(hostname=hostname, mask="id") return [result['id'] for result in results] def _get_ids_from_ip(self, ip): try: # Does it look like an ip address? socket.inet_aton(ip) except socket.error: return [] # Find the CCI via ip address. First try public ip, then private results = self.list_instances(public_ip=ip, mask="id") if results: return [result['id'] for result in results] results = self.list_instances(private_ip=ip, mask="id") if results: return [result['id'] for result in results]
[docs] def edit(self, id, userdata=None, hostname=None, domain=None, notes=None): """ Edit hostname, domain name, notes, and/or the user data of a CCI Parameters set to None will be ignored and not attempted to be updated. :param integer id: the instance ID to edit :param string userdata: user data on CCI to edit. If none exist it will be created :param string hostname: valid hostname :param string domain: valid domain namem :param string notes: notes about this particular CCI """ obj = {} if userdata: self.guest.setUserMetadata([userdata], id=id) if hostname: obj['hostname'] = hostname if domain: obj['domain'] = domain if notes: obj['notes'] = notes if not obj: return True return self.guest.editObject(obj, id=id)

Project Versions

This Page