コード例 #1
0
ファイル: disk.py プロジェクト: gavin-lai/clc-python-sdk
    def Grow(self, size):
        """Grow disk to the newly specified size.

		Size must be less than 1024 and must be greater than the current size.

		>>> clc.v2.Server("WA1BTDIX01").Disks().disks[2].Grow(30).WaitUntilComplete()
		0

		"""

        if size > 1024:
            raise (clc.CLCException("Cannot grow disk beyond 1024GB"))
        if size <= self.size:
            raise (clc.CLCException("New size must exceed current disk size"))
        # TODO - Raise exception if too much total size (4TB standard, 1TB HS)

        disk_set = [{
            'diskId': o.id,
            'sizeGB': o.size
        } for o in self.parent.disks if o != self]
        self.size = size
        disk_set.append({'diskId': self.id, 'sizeGB': self.size})
        self.parent.server.dirty = True
        return (clc.v2.Requests(clc.v2.API.Call(
            'PATCH', 'servers/%s/%s' %
            (self.parent.server.alias, self.parent.server.id),
            json.dumps([{
                "op": "set",
                "member": "disks",
                "value": disk_set
            }])),
                                alias=self.parent.server.alias))
コード例 #2
0
ファイル: server.py プロジェクト: bjfelton/clc-python-sdk
	def Create(name,template,group_id,network_id,cpu=None,memory=None,alias=None,password=None,ip_address=None,
	           storage_type="standard",type="standard",primary_dns=None,secondary_dns=None,
			   additional_disks=[],custom_fields=[],ttl=None,managed_os=False,description=None,
			   source_server_password=None,cpu_autoscale_policy_id=None,anti_affinity_policy_id=None,
			   packages=[]):  
		"""Creates a new server.

		https://www.centurylinkcloud.com/api-docs/v2/#servers-create-server

		cpu and memory are optional and if not provided we pull from the default server size values associated with
		the provided group_id.

		Set ttl as number of seconds before server is to be terminated.  Must be >3600

		>>> d = clc.v2.Datacenter()
		>>> clc.v2.Server.Create(name="api2",cpu=1,memory=1,
		                         group_id=d.Groups().Get("Default Group").id,
	                             template=d.Templates().Search("centos-6-64")[0].id,
						         network_id=d.Networks().networks[0].id).WaitUntilComplete()
		0

		"""

		if not alias:  alias = clc.v2.Account.GetAlias()

		if not cpu or not memory:
			group = clc.v2.Group(id=group_id,alias=alias)
			if not cpu and group.Defaults("cpu"):  cpu = group.Defaults("cpu")
			elif not cpu:  raise(clc.CLCException("No default CPU defined"))

			if not memory and group.Defaults("memory"):  memory = group.Defaults("memory")
			elif not memory:  raise(clc.CLCException("No default Memory defined"))
		if not description:  description = name
                if type.lower() == "standard" and storage_type.lower() not in ("standard","premium"):  raise(clc.CLCException("Invalid storage_type"))
                if type.lower() == "hyperscale" and storage_type.lower() != "hyperscale":  raise(clc.CLCException("Invalid type/storage_type combo"))
		if ttl and ttl<=3600: raise(clc.CLCException("ttl must be greater than 3600 seconds"))
		if ttl: ttl = clc.v2.time_utils.SecondsToZuluTS(int(time.time())+ttl)
		if type.lower() == "baremetal":  type = "bareMetal"
		# TODO - validate custom_fields as a list of dicts with an id and a value key
		# TODO - validate template exists
		# TODO - validate additional_disks as a list of dicts with a path, sizeGB, and type (partitioned,raw) keys
		# TODO - validate addition_disks path not in template reserved paths
		# TODO - validate antiaffinity policy id set only with type=hyperscale

		return(clc.v2.Requests(clc.v2.API.Call('POST','servers/%s' % (alias),
		         json.dumps({'name': name, 'description': description, 'groupId': group_id, 'sourceServerId': template,
							 'isManagedOS': managed_os, 'primaryDNS': primary_dns, 'secondaryDNS': secondary_dns, 
							 'networkId': network_id, 'ipAddress': ip_address, 'password': password,
							 'sourceServerPassword': source_server_password, 'cpu': cpu, 'cpuAutoscalePolicyId': cpu_autoscale_policy_id,
							 'memoryGB': memory, 'type': type, 'storageType': storage_type, 'antiAffinityPolicyId': anti_affinity_policy_id,
							 'customFields': custom_fields, 'additionalDisks': additional_disks, 'ttl': ttl, 'packages': packages})),
				 alias=alias))
コード例 #3
0
    def SetPassword(self, password):
        """Request change of password.

		The API request requires supplying the current password.  For this we issue a call
		to retrieve the credentials so note there will be an activity log for retrieving the
		credentials associated with any SetPassword entry

		>>> s.SetPassword("newpassword")

		"""

        # 0: {op: "set", member: "password", value: {current: " r`5Mun/vT:qZ]2?z", password: "******"}}
        if self.data['status'] != "active":
            raise (clc.CLCException(
                "Server must be powered on to change password"))

        return (clc.v2.Requests(clc.v2.API.Call(
            'PATCH',
            'servers/%s/%s' % (self.alias, self.id),
            json.dumps([{
                "op": "set",
                "member": "password",
                "value": {
                    "current": self.Credentials()['password'],
                    "password": password
                }
            }]),
            session=self.session),
                                alias=self.alias,
                                session=self.session))
コード例 #4
0
    def ConvertToTemplate(self, visibility, description=None, password=None):
        """Converts existing server to a template.

		visibility is one of private or shared.

		>>> d = clc.v2.Datacenter()
		>>> clc.v2.Server(alias='BTDI',id='WA1BTDIAPI207').ConvertToTemplate("private","my template")
		0

		"""

        if visibility not in ('private', 'shared'):
            raise (clc.CLCException(
                "Invalid visibility - must be private or shared"))
        if not password: password = self.Credentials()['password']
        if not description: description = self.description

        return (clc.v2.Requests(clc.v2.API.Call(
            'POST',
            'servers/%s/%s/convertToTemplate' % (self.alias, self.id),
            json.dumps({
                "description": description,
                "visibility": visibility,
                "password": password
            }),
            session=self.session),
                                alias=self.alias,
                                session=self.session))
コード例 #5
0
    def RestoreSnapshot(self, name=None):
        """Restores an existing Hypervisor level snapshot.

		Supply snapshot name to restore
		If no snapshot name is supplied will restore the first snapshot found

		>>> clc.v2.Server(alias='BTDI',id='WA1BTDIKRT02').RestoreSnapshot().WaitUntilComplete()
		0

		"""

        if not len(self.data['details']['snapshots']):
            raise (clc.CLCException("No snapshots exist"))
        if name is None: name = self.GetSnapshots()[0]

        name_links = [
            obj['links'] for obj in self.data['details']['snapshots']
            if obj['name'] == name
        ][0]
        return (clc.v2.Requests(clc.v2.API.Call(
            'POST',
            [obj['href'] for obj in name_links if obj['rel'] == 'restore'][0],
            session=self.session),
                                alias=self.alias,
                                session=self.session))
コード例 #6
0
    def CreateSnapshot(self, delete_existing=True, expiration_days=7):
        """Take a Hypervisor level snapshot retained for between 1 and 10 days (7 is default).
		Currently only one snapshop may exist at a time, thus will delete snapshots if one already
		exists before taking this snapshot.

		>>> clc.v2.Server("WA1BTDIAPI219").CreateSnapshot(2)
		<clc.APIv2.queue.Requests object at 0x10d106cd0>
		>>> _.WaitUntilComplete()
		0

		"""

        if len(self.data['details']['snapshots']):
            if delete_existing: self.DeleteSnapshot()
            else:
                raise (clc.CLCException(
                    "Snapshot already exists cannot take another"))

        return (clc.v2.Requests(clc.v2.API.Call(
            'POST',
            'operations/%s/servers/createSnapshot' % (self.alias), {
                'serverIds': self.id,
                'snapshotExpirationDays': expiration_days
            },
            session=self.session),
                                alias=self.alias,
                                session=self.session))
コード例 #7
0
    def Server(self):
        """Return server associated with this request.

		>>> d = clc.v2.Datacenter()
		>>> q = clc.v2.Server.Create(name="api2",cpu=1,memory=1,group_id=d.Groups().Get("Default Group").id,template=d.Templates().Search("centos-6-64")[0].id,network_id=d.Networks().networks[0].id,ttl=4000)
		>>> q.WaitUntilComplete()
		0
		>>> q.success_requests[0].Server()
		<clc.APIv2.server.Server object at 0x1095a8390>
		>>> print _
		VA1BTDIAPI214

		"""
        if self.context_key == 'newserver':
            server_id = clc.v2.API.Call('GET',
                                        self.context_val,
                                        session=self.session)['id']
            return (clc.v2.Server(id=server_id,
                                  alias=self.alias,
                                  session=self.session))
        elif self.context_key == 'server':
            return (clc.v2.Server(id=self.context_val,
                                  alias=self.alias,
                                  session=self.session))
        else:
            raise (clc.CLCException("%s object not server" % self.context_key))
コード例 #8
0
    def WaitUntilComplete(self, poll_freq=2, timeout=None):
        """Poll until status is completed.

		If status is 'notStarted' or 'executing' continue polling.
		If status is 'succeeded' return
		Else raise exception

		poll_freq option is in seconds

		"""
        start_time = time.time()
        while not self.time_completed:
            status = self.Status()
            if status == 'executing':
                if not self.time_executed: self.time_executed = time.time()
                if clc.v2.time_utils.TimeoutExpired(start_time, timeout):
                    raise clc.RequestTimeoutException(
                        'Timeout waiting for Request: {0}'.format(self.id),
                        status)

            elif status == 'succeeded':
                self.time_completed = time.time()
            elif status in ("failed", "resumed" or "unknown"):
                # TODO - need to ID best reaction for resumed status (e.g. manual intervention)
                self.time_completed = time.time()
                raise (clc.CLCException(
                    "%s %s execution %s" %
                    (self.context_key, self.context_val, status)))

            time.sleep(poll_freq)
コード例 #9
0
ファイル: server.py プロジェクト: bjfelton/clc-python-sdk
	def __init__(self,id,alias=None,server_obj=None):
		"""Create Server object.

		http://www.centurylinkcloud.com/api-docs/v2#servers-get-server

		#If parameters are populated then create object location.  
		#Else if only id is supplied issue a Get Policy call

		# successful creation
		>>> clc.v2.Server("CA3BTDICNTRLM01")
		<clc.APIv2.server.Server object at 0x10c28fe50>
		>>> print _
		CA3BTDICNTRLM01

		# error.  API returns 404 when server does not exist, we raise exception
		>>> clc.v2.Server(alias='BTDI',id='WA1BTDIKRT01')
		clc.CLCException: Server does not exist

		"""

		self.id = id
		self.capabilities = None
		self.disks = None
		self.public_ips = None
		self.dirty = False

		if alias:  self.alias = alias
		else:  self.alias = clc.v2.Account.GetAlias()

		if server_obj:  self.data = server_obj
		else:  
			try:
				self.Refresh()
			except clc.APIFailedResponse as e:
				if e.response_status_code==404:  raise(clc.CLCException("Server does not exist"))
コード例 #10
0
ファイル: network.py プロジェクト: bjfelton/clc-python-sdk
	def __init__(self,alias=None,location=None,networks_lst=None):
		if alias:  self.alias = alias
		else:  self.alias = clc.v2.Account.GetAlias()

		self.networks = []
		if networks_lst:
			for network in networks_lst:
				self.networks.append(Network(id=network['networkId'],alias=network['accountID'],network_obj=network))
		elif location:
			self._Load(location)
		else:
			raise(clc.CLCException("Networks object requires location or networks_lst"))
コード例 #11
0
    def __init__(self, id, alias=None, network_obj=None, session=None):
        """Create Network object."""

        self.id = id
        self.type = type
        self.dirty = False
        self.data = network_obj
        self.session = session

        if alias: self.alias = alias
        else: self.alias = clc.v2.Account.GetAlias(session=self.session)

        if network_obj: self.name = network_obj['name']
        else:
            try:
                self.Refresh()
            except clc.APIFailedResponse as e:
                if e.response_status_code == 404:
                    raise (clc.CLCException("Network does not exist"))
                else:
                    raise (clc.CLCException(
                        "An error occurred while creating the Network object"))
コード例 #12
0
	def Get(self,key):
		"""Get group by providing name, ID, description or other unique key.

		If key is not unique and finds multiple matches only the first
		will be returned

		>>> clc.v2.Datacenter().Groups().Get("Default Group")
		<clc.APIv2.group.Group object at 0x1065e5250>

		"""

		for group in self.groups:
			if group.id.lower() == key.lower():  return(group)
			elif group.name.lower() == key.lower():  return(group)
			elif group.description.lower() == key.lower():  return(group)

		raise(clc.CLCException("Group not found"))	# No Match
コード例 #13
0
    def Add(self, size, path=None, type="partitioned"):
        """Add new disk.

		This request will error if disk is protected and cannot be removed (e.g. a system disk)

		# Partitioned disk
		>>> clc.v2.Server("WA1BTDIX01").Disks().Add(size=20,path=None,type="raw").WaitUntilComplete()
		0

		# Raw disk
		>>> clc.v2.Server("WA1BTDIX01").Disks().Add(size=20,path=None,type="raw").WaitUntilComplete()
		0

		"""

        if type == "partitioned" and not path:
            raise (clc.CLCException("Must specify path to mount new disk"))
        # TODO - Raise exception if too many disks
        # TODO - Raise exception if too much total size (4TB standard, 1TB HS)

        disk_set = [{'diskId': o.id, 'sizeGB': o.size} for o in self.disks]
        disk_set.append({'sizeGB': size, 'type': type, 'path': path})
        self.disks.append(
            Disk(id=int(time.time()),
                 parent=self,
                 disk_obj={
                     'sizeGB': size,
                     'partitionPaths': [path]
                 },
                 session=self.session))

        self.size = size
        self.server.dirty = True
        return (clc.v2.Requests(clc.v2.API.Call(
            'PATCH',
            'servers/%s/%s' % (self.server.alias, self.server.id),
            json.dumps([{
                "op": "set",
                "member": "disks",
                "value": disk_set
            }]),
            session=self.session),
                                alias=self.server.alias,
                                session=self.session))
コード例 #14
0
    def __init__(self, id_, policy_obj=None, alias=None, session=None):
        self.id = id_
        self.dirty = False
        self.session = session

        if alias:
            self.alias = alias
        else:
            self.alias = clc.v2.Account.GetAlias(session=self.session)

        if policy_obj:
            self.data = policy_obj
        else:
            try:
                self.Refresh()
            except clc.APIFailedResponse as e:
                if e.response_status_code == 404:
                    raise (clc.CLCException(
                        "Horizontal Autoscale Policy does not exist"))
コード例 #15
0
	def WaitUntilComplete(self,poll_freq=2):
		"""Poll until status is completed.

		If status is 'notStarted' or 'executing' continue polling.
		If status is 'succeeded' return
		Else raise exception

		poll_freq option is in seconds

		"""
		while not self.time_completed:
			status = self.Status()
			if status == 'executing':
				if not self.time_executed:  self.time_executed = time.time()
			elif status == 'succeeded':
				self.time_completed = time.time()
			elif status in ("failed", "resumed" or "unknown"):
				# TODO - need to ID best reaction for resumed status (e.g. manual intervention)
				self.time_completed = time.time()
				raise(clc.CLCException("%s %s execution %s" % (self.context_key,self.context_val,status)))

			time.sleep(poll_freq)
コード例 #16
0
    def __init__(self, requests_lst, alias=None):
        """Create Requests object.

		Treats one or more requests as an atomic unit.
		e.g. if performing a simulated group operation then succeed
		or fail as a group

		"""

        if alias: self.alias = alias
        else: self.alias = clc.v2.Account.GetAlias()

        self.requests = []
        self.error_requests = []
        self.success_requests = []

        # Some requests_lst responses look different than others depending on the call (Create snapshot vs. delete snapshot)
        # Add min-wrapper onto those missing it
        if requests_lst is None:
            raise (Exception("Unexpected requests response"))
        elif 'isQueued' in requests_lst:
            requests_lst = [requests_lst]
        elif 'href' in requests_lst:
            requests_lst = [{'isQueued': True, 'links': [requests_lst]}]

        for r in requests_lst:

            if 'server' in r and len(r['server']) < 6:
                # Hopefully this captures only new server builds, TODO find a better way to ID these
                for link in r['links']:
                    if re.search("/v2/servers/", link['href']):
                        context_key = "newserver"
                        context_val = link['href']
                        break
            elif 'server' in r:
                context_key = "server"
                context_val = r['server']
            else:
                context_key = "Unknown"
                context_val = "Unknown"

            if r['isQueued']:
                self.requests.append(
                    Request([
                        obj['id'] for obj in r['links']
                        if obj['rel'] == 'status'
                    ][0],
                            alias=self.alias,
                            request_obj={
                                'context_key': context_key,
                                'context_val': context_val
                            }))
            else:
                # If we're dealing with a list of responses and we have an error with one I'm not sure how
                # folks would expect this to behave.  If server is already in desired state thus the request
                # fails that shouldn't be an exception.  If we're running against n tasks and just one has an
                # issue we need a reasonable way to report on the error but also follow the remaining tasks.
                #
                # For no-op failures we just won't create an object and our queue wait time will be faster.
                # For actual failures we'll wait until all tasks have reached a conclusion then .....
                if r['errorMessage'] == "The server already in desired state.":
                    pass
                elif r['errorMessage'] == "The operation cannot be queued because the server cannot be found or it is not in a valid state.":
                    raise (Exception("do we pass on this or take action? %s" %
                                     r['errorMessage']))
                else:
                    # TODO - need to ID other reasons for not queuing and known reasons don't raise out of the
                    #        entire process
                    raise (clc.CLCException(
                        "%s '%s' not added to queue: %s" %
                        (context_key, context_val, r['errorMessage'])))