def destroy_volume(self, volume): """ Destroys disk volume for the Linode. Linode id is to be provided as extra["LinodeId"] whithin :class:`StorageVolume`. It can be retrieved by :meth:`libcloud.compute.drivers.linode.LinodeNodeDriver\ .ex_list_volumes`. :param volume: Volume to be destroyed :type volume: :class:`StorageVolume` :rtype: ``bool`` """ if not isinstance(volume, StorageVolume): raise LinodeException(0xFD, "Invalid volume instance") if volume.extra["LINODEID"] is None: raise LinodeException(0xFD, "Missing LinodeID") params = { "api_action": "linode.disk.delete", "LinodeID": volume.extra["LINODEID"], "DiskID": volume.id, } self.connection.request(API_ROOT, params=params) return True
def ex_create_volume(self, size, name, node, fs_type): """ Create disk for the Linode. :keyword size: Size of volume in megabytes (required) :type size: ``int`` :keyword name: Name of the volume to be created :type name: ``str`` :keyword node: Node to attach volume to. :type node: :class:`Node` :keyword fs_type: The formatted type of this disk. Valid types are: ext3, ext4, swap, raw :type fs_type: ``str`` :return: StorageVolume representing the newly-created volume :rtype: :class:`StorageVolume` """ # check node if not isinstance(node, Node): raise LinodeException(0xFD, "Invalid node instance") # check space available total_space = node.extra['TOTALHD'] existing_volumes = self.ex_list_volumes(node) used_space = 0 for volume in existing_volumes: used_space = used_space + volume.size available_space = total_space - used_space if available_space < size: raise LinodeException( 0xFD, "Volume size too big. Available space\ %d" % available_space) # check filesystem type if fs_type not in self._linode_disk_filesystems: raise LinodeException(0xFD, "Not valid filesystem type") params = { "api_action": "linode.disk.create", "LinodeID": node.id, "Label": name, "Type": fs_type, "Size": size } data = self.connection.request(API_ROOT, params=params).objects[0] volume = data["DiskID"] # Make a volume out of it and hand it back params = { "api_action": "linode.disk.list", "LinodeID": node.id, "DiskID": volume } data = self.connection.request(API_ROOT, params=params).objects[0] return self._to_volumes(data)[0]
def linode_set_datacenter(self, dc): """ Set the default datacenter for Linode creation Since Linodes must be created in a facility, this function sets the default that :class:`create_node` will use. If a location keyword is not passed to :class:`create_node`, this method must have already been used. :keyword dc: the datacenter to create Linodes in unless specified :type dc: :class:`NodeLocation` :rtype: ``bool`` """ did = dc.id params = {"api_action": "avail.datacenters"} data = self.connection.request(API_ROOT, params=params).objects[0] for datacenter in data: if did == dc["DATACENTERID"]: self.datacenter = did return dcs = ", ".join([d["DATACENTERID"] for d in data]) self.datacenter = None raise LinodeException(0xFD, "Invalid datacenter (use one of %s)" % dcs)
def ex_list_volumes(self, node, disk_id=None): """ List existing disk volumes for for given Linode. :keyword node: Node to list disk volumes for. (required) :type node: :class:`Node` :keyword disk_id: Id for specific disk volume. (optional) :type disk_id: ``int`` :rtype: ``list`` of :class:`StorageVolume` """ if not isinstance(node, Node): raise LinodeException(0xFD, "Invalid node instance") params = { "api_action": "linode.disk.list", "LinodeID": node.id } # Add param if disk_id was specified if disk_id is not None: params["DiskID"] = disk_id data = self.connection.request(API_ROOT, params=params).objects[0] return self._to_volumes(data)
def create_node(self, **kwargs): """Create a new Linode, deploy a Linux distribution, and boot This call abstracts much of the functionality of provisioning a Linode and getting it booted. A global grant to add Linodes to the account is required, as this call will result in a billing charge. Note that there is a safety valve of 5 Linodes per hour, in order to prevent a runaway script from ruining your day. @keyword name: the name to assign the Linode (mandatory) @type name: C{str} @keyword image: which distribution to deploy on the Linode (mandatory) @type image: L{NodeImage} @keyword size: the plan size to create (mandatory) @type size: L{NodeSize} @keyword auth: an SSH key or root password (mandatory) @type auth: L{NodeAuthSSHKey} or L{NodeAuthPassword} @keyword location: which datacenter to create the Linode in @type location: L{NodeLocation} @keyword ex_swap: size of the swap partition in MB (128) @type ex_swap: C{int} @keyword ex_rsize: size of the root partition in MB (plan size - swap). @type ex_rsize: C{int} @keyword ex_kernel: a kernel ID from avail.kernels (Latest 2.6 Stable). @type ex_kernel: C{str} @keyword ex_payment: one of 1, 12, or 24; subscription length (1) @type ex_payment: C{int} @keyword ex_comment: a small comment for the configuration (libcloud) @type ex_comment: C{str} @keyword ex_private: whether or not to request a private IP (False) @type ex_private: C{bool} @keyword lconfig: what to call the configuration (generated) @type lconfig: C{str} @keyword lroot: what to call the root image (generated) @type lroot: C{str} @keyword lswap: what to call the swap space (generated) @type lswap: C{str} @return: a L{Node} representing the newly-created Linode """ name = kwargs["name"] image = kwargs["image"] size = kwargs["size"] auth = kwargs["auth"] # Pick a location (resolves LIBCLOUD-41 in JIRA) if "location" in kwargs: chosen = kwargs["location"].id elif self.datacenter: chosen = self.datacenter else: raise LinodeException(0xFB, "Need to select a datacenter first") # Step 0: Parameter validation before we purchase # We're especially careful here so we don't fail after purchase, rather # than getting halfway through the process and having the API fail. # Plan ID plans = self.list_sizes() if size.id not in [p.id for p in plans]: raise LinodeException(0xFB, "Invalid plan ID -- avail.plans") # Payment schedule payment = "1" if "ex_payment" not in kwargs else str(kwargs["ex_payment"]) if payment not in ["1", "12", "24"]: raise LinodeException(0xFB, "Invalid subscription (1, 12, 24)") ssh = None root = None # SSH key and/or root password if isinstance(auth, NodeAuthSSHKey): ssh = auth.pubkey elif isinstance(auth, NodeAuthPassword): root = auth.password if not ssh and not root: raise LinodeException(0xFB, "Need SSH key or root password") if not root is None and len(root) < 6: raise LinodeException(0xFB, "Root password is too short") # Swap size try: swap = 128 if "ex_swap" not in kwargs else int(kwargs["ex_swap"]) except: raise LinodeException(0xFB, "Need an integer swap size") # Root partition size imagesize = (size.disk - swap) if "ex_rsize" not in kwargs else \ int(kwargs["ex_rsize"]) if (imagesize + swap) > size.disk: raise LinodeException(0xFB, "Total disk images are too big") # Distribution ID distros = self.list_images() if image.id not in [d.id for d in distros]: raise LinodeException(0xFB, "Invalid distro -- avail.distributions") # Kernel if "ex_kernel" in kwargs: kernel = kwargs["ex_kernel"] else: if image.extra['64bit']: kernel = 111 if image.extra['pvops'] else 107 else: kernel = 110 if image.extra['pvops'] else 60 params = { "api_action": "avail.kernels" } kernels = self.connection.request(API_ROOT, params=params).objects[0] if kernel not in [z["KERNELID"] for z in kernels]: raise LinodeException(0xFB, "Invalid kernel -- avail.kernels") # Comments comments = "Created by Apache libcloud <http://www.libcloud.org>" if \ "ex_comment" not in kwargs else kwargs["ex_comment"] # Labels label = { "lconfig": "[%s] Configuration Profile" % name, "lroot": "[%s] %s Disk Image" % (name, image.name), "lswap": "[%s] Swap Space" % name } for what in ["lconfig", "lroot", "lswap"]: if what in kwargs: label[what] = kwargs[what] # Step 1: linode.create params = { "api_action": "linode.create", "DatacenterID": chosen, "PlanID": size.id, "PaymentTerm": payment } data = self.connection.request(API_ROOT, params=params).objects[0] linode = { "id": data["LinodeID"] } # Step 1b. linode.update to rename the Linode params = { "api_action": "linode.update", "LinodeID": linode["id"], "Label": name } self.connection.request(API_ROOT, params=params) # Step 1c. linode.ip.addprivate if it was requested if "ex_private" in kwargs and kwargs["ex_private"]: params = { "api_action": "linode.ip.addprivate", "LinodeID": linode["id"] } self.connection.request(API_ROOT, params=params) # Step 2: linode.disk.createfromdistribution if not root: root = binascii.b2a_base64(os.urandom(8)).decode('ascii') params = { "api_action": "linode.disk.createfromdistribution", "LinodeID": linode["id"], "DistributionID": image.id, "Label": label["lroot"], "Size": imagesize, "rootPass": root, } if ssh: params["rootSSHKey"] = ssh data = self.connection.request(API_ROOT, params=params).objects[0] linode["rootimage"] = data["DiskID"] # Step 3: linode.disk.create for swap params = { "api_action": "linode.disk.create", "LinodeID": linode["id"], "Label": label["lswap"], "Type": "swap", "Size": swap } data = self.connection.request(API_ROOT, params=params).objects[0] linode["swapimage"] = data["DiskID"] # Step 4: linode.config.create for main profile disks = "%s,%s,,,,,,," % (linode["rootimage"], linode["swapimage"]) params = { "api_action": "linode.config.create", "LinodeID": linode["id"], "KernelID": kernel, "Label": label["lconfig"], "Comments": comments, "DiskList": disks } data = self.connection.request(API_ROOT, params=params).objects[0] linode["config"] = data["ConfigID"] # Step 5: linode.boot params = { "api_action": "linode.boot", "LinodeID": linode["id"], "ConfigID": linode["config"] } self.connection.request(API_ROOT, params=params) # Make a node out of it and hand it back params = { "api_action": "linode.list", "LinodeID": linode["id"] } data = self.connection.request(API_ROOT, params=params).objects[0] return self._to_nodes(data)
def create_node(self, name, image, size, auth, location=None, ex_swap=None, ex_rsize=None, ex_kernel=None, ex_payment=None, ex_comment=None, ex_private=False, lconfig=None, lroot=None, lswap=None): """Create a new Linode, deploy a Linux distribution, and boot This call abstracts much of the functionality of provisioning a Linode and getting it booted. A global grant to add Linodes to the account is required, as this call will result in a billing charge. Note that there is a safety valve of 5 Linodes per hour, in order to prevent a runaway script from ruining your day. :keyword name: the name to assign the Linode (mandatory) :type name: ``str`` :keyword image: which distribution to deploy on the Linode (mandatory) :type image: :class:`NodeImage` :keyword size: the plan size to create (mandatory) :type size: :class:`NodeSize` :keyword auth: an SSH key or root password (mandatory) :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword` :keyword location: which datacenter to create the Linode in :type location: :class:`NodeLocation` :keyword ex_swap: size of the swap partition in MB (128) :type ex_swap: ``int`` :keyword ex_rsize: size of the root partition in MB (plan size - swap). :type ex_rsize: ``int`` :keyword ex_kernel: a kernel ID from avail.kernels (Latest 2.6 Stable). :type ex_kernel: ``str`` :keyword ex_payment: one of 1, 12, or 24; subscription length (1) :type ex_payment: ``int`` :keyword ex_comment: a small comment for the configuration (libcloud) :type ex_comment: ``str`` :keyword ex_private: whether or not to request a private IP (False) :type ex_private: ``bool`` :keyword lconfig: what to call the configuration (generated) :type lconfig: ``str`` :keyword lroot: what to call the root image (generated) :type lroot: ``str`` :keyword lswap: what to call the swap space (generated) :type lswap: ``str`` :return: Node representing the newly-created Linode :rtype: :class:`Node` """ auth = self._get_and_check_auth(auth) # Pick a location (resolves LIBCLOUD-41 in JIRA) if location: chosen = location.id elif self.datacenter: chosen = self.datacenter else: raise LinodeException(0xFB, "Need to select a datacenter first") # Step 0: Parameter validation before we purchase # We're especially careful here so we don't fail after purchase, rather # than getting halfway through the process and having the API fail. # Plan ID plans = self.list_sizes() if size.id not in [p.id for p in plans]: raise LinodeException(0xFB, "Invalid plan ID -- avail.plans") # Payment schedule payment = "1" if not ex_payment else str(ex_payment) if payment not in ["1", "12", "24"]: raise LinodeException(0xFB, "Invalid subscription (1, 12, 24)") ssh = None root = None # SSH key and/or root password if isinstance(auth, NodeAuthSSHKey): ssh = auth.pubkey # pylint: disable=no-member elif isinstance(auth, NodeAuthPassword): root = auth.password if not ssh and not root: raise LinodeException(0xFB, "Need SSH key or root password") if root is not None and len(root) < 6: raise LinodeException(0xFB, "Root password is too short") # Swap size try: swap = 128 if not ex_swap else int(ex_swap) except Exception: raise LinodeException(0xFB, "Need an integer swap size") # Root partition size imagesize = (size.disk - swap) if not ex_rsize else\ int(ex_rsize) if (imagesize + swap) > size.disk: raise LinodeException(0xFB, "Total disk images are too big") # Distribution ID distros = self.list_images() if image.id not in [d.id for d in distros]: raise LinodeException(0xFB, "Invalid distro -- avail.distributions") # Kernel if ex_kernel: kernel = ex_kernel else: if image.extra['64bit']: # For a list of available kernel ids, see # https://www.linode.com/kernels/ kernel = 138 else: kernel = 137 params = {"api_action": "avail.kernels"} kernels = self.connection.request(API_ROOT, params=params).objects[0] if kernel not in [z["KERNELID"] for z in kernels]: raise LinodeException(0xFB, "Invalid kernel -- avail.kernels") # Comments comments = "Created by Apache libcloud <https://www.libcloud.org>" if\ not ex_comment else ex_comment # Step 1: linode.create params = { "api_action": "linode.create", "DatacenterID": chosen, "PlanID": size.id, "PaymentTerm": payment } data = self.connection.request(API_ROOT, params=params).objects[0] linode = {"id": data["LinodeID"]} # Step 1b. linode.update to rename the Linode params = { "api_action": "linode.update", "LinodeID": linode["id"], "Label": name } self.connection.request(API_ROOT, params=params) # Step 1c. linode.ip.addprivate if it was requested if ex_private: params = { "api_action": "linode.ip.addprivate", "LinodeID": linode["id"] } self.connection.request(API_ROOT, params=params) # Step 1d. Labels # use the linode id as the name can be up to 63 chars and the labels # are limited to 48 chars label = { "lconfig": "[%s] Configuration Profile" % linode["id"], "lroot": "[%s] %s Disk Image" % (linode["id"], image.name), "lswap": "[%s] Swap Space" % linode["id"] } if lconfig: label['lconfig'] = lconfig if lroot: label['lroot'] = lroot if lswap: label['lswap'] = lswap # Step 2: linode.disk.createfromdistribution if not root: root = binascii.b2a_base64(os.urandom(8)).decode('ascii').strip() params = { "api_action": "linode.disk.createfromdistribution", "LinodeID": linode["id"], "DistributionID": image.id, "Label": label["lroot"], "Size": imagesize, "rootPass": root, } if ssh: params["rootSSHKey"] = ssh data = self.connection.request(API_ROOT, params=params).objects[0] linode["rootimage"] = data["DiskID"] # Step 3: linode.disk.create for swap params = { "api_action": "linode.disk.create", "LinodeID": linode["id"], "Label": label["lswap"], "Type": "swap", "Size": swap } data = self.connection.request(API_ROOT, params=params).objects[0] linode["swapimage"] = data["DiskID"] # Step 4: linode.config.create for main profile disks = "%s,%s,,,,,,," % (linode["rootimage"], linode["swapimage"]) params = { "api_action": "linode.config.create", "LinodeID": linode["id"], "KernelID": kernel, "Label": label["lconfig"], "Comments": comments, "DiskList": disks } if ex_private: params['helper_network'] = True params['helper_distro'] = True data = self.connection.request(API_ROOT, params=params).objects[0] linode["config"] = data["ConfigID"] # Step 5: linode.boot params = { "api_action": "linode.boot", "LinodeID": linode["id"], "ConfigID": linode["config"] } self.connection.request(API_ROOT, params=params) # Make a node out of it and hand it back params = {"api_action": "linode.list", "LinodeID": linode["id"]} data = self.connection.request(API_ROOT, params=params).objects[0] nodes = self._to_nodes(data) if len(nodes) == 1: node = nodes[0] if getattr(auth, "generated", False): node.extra['password'] = auth.password return node return None