Example #1
0
    def __init__(self, key='', secret='', account="default"):
        """Constructor.

        key and secret params are optional. If you will specify both of them, GoGridManager
        will use these credentials to use GoGrid API. If at least one of the params will be
        missing, it will try to use configuration file. Configuration file is located
        at ~/.ggrc and contains only one line of the format: "api_key:secret". You can get more
        details on obtaining GoGrid API key at the GoGrid wiki.
        """
        self.gogrid_client = GoGridClient(key, secret, account=account)
Example #2
0
class GoGridManager:
    """
    The main class to accessing GoGrid API methods.
    """

    def __init__(self, key='', secret='', account="default"):
        """Constructor.

        key and secret params are optional. If you will specify both of them, GoGridManager
        will use these credentials to use GoGrid API. If at least one of the params will be
        missing, it will try to use configuration file. Configuration file is located
        at ~/.ggrc and contains only one line of the format: "api_key:secret". You can get more
        details on obtaining GoGrid API key at the GoGrid wiki.
        """
        self.gogrid_client = GoGridClient(key, secret, account=account)

######################
######################

    def get_ips(self, type="all", state=None, datacenter=None):
        """
        Returns a list of IPs available for the current account.

        @type type: string
        @param type: A type of the IP address. Can be "Public", "Private" and "All" which means both private and public are OK
        @type state: string
        @param state: A state of the IP, can be one of "Assigned" or "Unassigned". By default returns all IPs
        @type datacenter: string
        @param datacenter: Datacenter you wish to obtain addresses from, by default use all datacenters
        @rtype: list
        @return: a list of L{GGIp<GGIp>} objects matching the query
        """

        param_dict = {}

        if type != "all":
            param_dict["ip.type"] = type

        if state is not None:
            param_dict["ip.state"] = state

        if datacenter is not None:
            param_dict["datacenter"] = datacenter

        data = self.gogrid_client.sendAPIRequest("grid/ip/list", param_dict)

        doc = xml.dom.minidom.parseString(data)

        ips = []
        object_nodes = doc.getElementsByTagName("object")

        for obj in object_nodes:
            if "ip" == obj.getAttribute("name"):
                ips.append(self._parse_ip_object(obj))

        return ips

    def get_server(self, id=None, name=None, server=None):
        """
        Returns an information about single server

        @type id: string
        @param id: You might specify id to search for a server by id
        @type name: string
        @param name: You might specify name to search for a server by its name
        @type server: string
        @param server: You might specify server to search server by both id or name
        @rtype: L{GGServer<GGServer>}
        @return: instace of corresponding L{GGServer<GGServer>}
        """
        param_dict = {}

        if id is not None:
            param_dict["id"] = id
        elif name is not None:
            param_dict["name"] = name
        elif server is not None:
            param_dict["server"] = server

        if len(param_dict) == 0:
            raise Exception('At least one of id, name or server should be not None!')

        data = self.gogrid_client.sendAPIRequest("grid/server/get", param_dict)

        doc = xml.dom.minidom.parseString(data)

        return self._parse_server_object(doc.getElementsByTagName("object")[0])

    def get_servers(self):
        """
        Returns a list of servers currently deployed

        @rtype: list
        @return: a list of L{GGServer<GGServer>} objects representing currently running servers
        """

        param_dict = {} 

        data = self.gogrid_client.sendAPIRequest("grid/server/list", param_dict)

        doc = xml.dom.minidom.parseString(data)

        servers = []
        object_nodes = doc.getElementsByTagName("object")

        for obj in object_nodes:
            if "server" == obj.getAttribute("name"):
                servers.append(self._parse_server_object(obj))

        return servers

    def get_image(self, id, name=None, image=None):
        """
        Returns an information about single image

        @type id: string
        @param id: You might specify id to search for a image by id
        @type name: string
        @param name: You might specify name to search for a image by its name
        @type server: string
        @param server: You might specify server to search image by both id or name
        @rtype: L{GGServer<GGImage>}
        @return: instace of corresponding L{GGServer<GGImage>}
        """
        param_dict = {}

        if id is not None:
            param_dict["id"] = id
        elif name is not None:
            param_dict["name"] = name
        elif server is not None:
            param_dict["server"] = server

        if len(param_dict) == 0:
            raise Exception('At least one of id, name or server should be not None!')

        data = self.gogrid_client.sendAPIRequest("grid/image/get", param_dict)

        doc = xml.dom.minidom.parseString(data)

        return self._parse_serverimage_object(doc.getElementsByTagName("object")[0])

    def get_images(self):
        """
        Returns a list of available server images (i.e. Operating System templates)

        @rtype: list
        @return: a list of L{GGImage<GGImage>} objects representing currently available OS templates
        """
        data = self.gogrid_client.sendAPIRequest("grid/image/list", {})

        doc = xml.dom.minidom.parseString(data)

        images = []
        object_nodes = doc.getElementsByTagName("object")

        for obj in object_nodes:
            if "serverimage" != obj.getAttribute("name"):
                continue

            images.append(self._parse_serverimage_object(obj))

        return images

    def get_passwords(self):
        """
        Returns a list of passwords for the currently running servers

        @rtype: list
        @return: a list of L{GGPassword<GGPassword>} objects containing passwords for the currently running servers
        """
        data = self.gogrid_client.sendAPIRequest("support/password/list", {})

        doc = xml.dom.minidom.parseString(data)

        passwords = []
        object_nodes = doc.getElementsByTagName("object")

        for obj in object_nodes:
            if "password" == obj.getAttribute("name"):
                passwords.append(self._parse_password_object(obj))

        return passwords

    def get_job(self, id):
        """Returns information about single job."""

        data = self.gogrid_client.sendAPIRequest("grid/job/get", {"id": id})

        doc = xml.dom.minidom.parseString(data)

        return self._parse_job_object(doc.getElementsByTagName("object")[0])

    def get_jobs(self, num_items=20):
        """Returns a list of jobs"""

        data = self.gogrid_client.sendAPIRequest("grid/job/list", 
            {"num_items": num_items})

        doc = xml.dom.minidom.parseString(data)

        jobs = []
        object_nodes = doc.getElementsByTagName("object")

        for obj in object_nodes:
            if "job" == obj.getAttribute("name"):
                jobs.append(self._parse_job_object(obj))

        return jobs

    def save_image(self, name, server, descr=None):
        """
        A method to save sendbox image to cloud storage
        to be able to use it as a server image.

        @type name: string
        @param name: the friendly name of the  server image to be saved 
        @type server: instace to be used as an image. You can provide either
         symbolic name or numeric server id
        @type descr: string
        @param descr: optional literal description of the image
        """

        param_dict = {"friendlyName": name, "server": server}

        if descr is not None:
            param_dict["description"] = descr

        response = self.gogrid_client.sendAPIRequest("grid/image/save", param_dict)

        doc = xml.dom.minidom.parseString(response)

        return self._parse_serverimage_object(doc.getElementsByTagName("object")[0])

    def delete_image(self, id, name, image):
        """
        A method to delete server image.

        @type id: integer
        @param id: numeric id of the image to remove
        @type name: string
        @param name: literal name of the image to remove
        @type image: string
        #param image: id or name of the image to be removed (i.e. this value can hold both id or name of the image)
        @rtype: L{GGServer<GGImage>}
        @return: a L{GGServer<GGImage>} object corresponding to the deleted server
        """

        if id is not None:
            param_dict = {'id': id}
        elif name is not None:
            param_dict = {'name': name}
        else:
            param_dict = {'image': image}

        response = self.gogrid_client.sendAPIRequest("grid/image/delete", param_dict)

        doc = xml.dom.minidom.parseString(response)

        return self._parse_serverimage_object(doc.getElementsByTagName("object")[0])

    def add_server(self, name, image, ram, ip, descr=None, sandbox=False):
        """
        A method to add a new server.

        @type name: string
        @param name: desired name of the newely created server
        @type image: string
        @param image: image to be used for the server. You can use either symbolic name or numeric image id, but even
        if you specify image by id, it should still be string
        @type ram: string
        @param ram: desired amount of RAM for the server
        @type ip: string
        @param ip: IP address to be assigned to the server. Remember that you have to specify public IP address, private address will not work here
        @type descr: string
        @param descr: optional literal description of the server, may be omitted
        @rtype: L{GGServer<GGServer>}
        @return: a L{GGServer<GGServer>} object representing newely created server
        """

        param_dict = {"name": name, "image": image, "ram": ram, "ip": ip}

        if descr is not None:
            param_dict["description"] = descr

        if sandbox:
            param_dict['isSandbox'] = "true"


        response = self.gogrid_client.sendAPIRequest("grid/server/add", param_dict)

        doc = xml.dom.minidom.parseString(response)

        return self._parse_server_object(doc.getElementsByTagName("object")[0])

    def delete_server(self, id, name):
        """
        A method to remove server. It can delete server both by id and name and access both
        these parameters. Be sure to specify at least one of them. If you specify _both_, id
        will be preferred.

        @type id: integer
        @param id: numeric id of the server to remove
        @type name: string
        @param name: literal name of the server to remove
        @rtype: L{GGServer<GGServer>}
        @return: a L{GGServer<GGServer>} object corresponding to the deleted server
        """
        if id is not None:
            param_dict = {'id': id}
        else:
            param_dict = {'name': name} 

        # XXX to raise an exception if both fields are None

        response = self.gogrid_client.sendAPIRequest("grid/server/delete", param_dict)

        doc = xml.dom.minidom.parseString(response)

        return self._parse_server_object(doc.getElementsByTagName("object")[0])

    def power_server(self, id, name, action):
        """A method to start/stop/reboot server.

        You can identify server by either id or name. So you have to specify at least one of these params.
        If you specify both, id would be prefered.

        @param id: numeric id of the server
        @type id: string
        @param name: liternal name of the server
        @type name: string
        @param action: name of the operation to perform on the server. Possible operations are:

            1. "on" - power server on
            2. "off" - turns server off (not gracefully)
            3. "restart" - reboots server
        @type action: string

        @rtype: L{GGServer<GGServer>}
        @return: a L{GGServer<GGServer>} object representing a server operation was pefrormed on
        """
        if id is not None:
            param_dict = {"id": id}
        else:
            param_dict = {"name": name}

        param_dict["power"] = action

        response = self.gogrid_client.sendAPIRequest("grid/server/power", param_dict)

        doc = xml.dom.minidom.parseString(response)

        return self._parse_server_object(doc.getElementsByTagName("object")[0])

    def get_billing(self):
        """
        A method that returns account billing information

        @rtype: dict
        @return: dict with various billing information
        """
        response = self.gogrid_client.sendAPIRequest("myaccount/billing/get", {})

        doc = xml.dom.minidom.parseString(response)

        fields = ["startDate", "endDate", 
                "memoryAllotment", "memoryInUse", "memoryAccrued",
                "memoryOverage", "memoryOverageCharge",
                "transferAllotment", "transferAccrued",
                "transferOverage", "transferOverageCharge"]
        result = {}

        for node in doc.getElementsByTagName("object")[0].childNodes:
            if node.ELEMENT_NODE == node.nodeType:
                if "attribute" == node.nodeName:
                    name = node.getAttribute("name")

                    if name in fields:
                        result[name] = self._get_text(node)

        return result

    def _get_text(self, object):
        text = []
        for child in object.childNodes:
            if child.TEXT_NODE == child.nodeType:
                text.append(child.data)

        return ''.join(text)

    def _parse_serverimage_object(self, object):
        image = GGImage()

        mappings = {'id': 'id',
                'name': 'name',
                'friendlyName': 'friendlyName',
                'description': 'descr',
                'location': 'location',
                'createdTime': 'createdTime',
                'updatedTime': 'updatedTime'}
        boolean_mappings = {'isActive': 'isActive',
                'isPublic': 'isPublic'}

        for child in object.childNodes:
            if child.ELEMENT_NODE == child.nodeType:
                if "attribute" == child.nodeName:
                    name, value = child.getAttribute("name"), self._get_text(child)

                    if name in mappings:
                        setattr(image, mappings[name], value)
                    elif name in boolean_mappings:
                        setattr(image, boolean_mappings[name], value == "true")
                    elif "state" == name:
                        for grandchild in child.childNodes[0].childNodes:
                             if "attribute" == grandchild.nodeName:
                                  if "name" == grandchild.getAttribute("name"):
                                      image.state = self._get_text(grandchild)

        return image

    def _parse_ip_object(self, object):
        ip = GGIp()

        fields = ['id', 'ip', 'subnet']
        boolean_fields = ['public']

        for child in object.childNodes:
            if child.ELEMENT_NODE == child.nodeType:
                if "attribute" == child.nodeName:
                    name, value = child.getAttribute("name"), self._get_text(child)

                    if name in fields:
                        setattr(ip, name, value)
                    elif name in boolean_fields:
                        setattr(ip, name, value == "true")
                    elif "datacenter" == name:
                        for grandchild in child.childNodes[0].childNodes:
                            if "attribute" == grandchild.nodeName:
                                if "name" == grandchild.getAttribute("name"):
                                    ip.datacenter = self._get_text(grandchild)

        return ip

    def _parse_server_object(self, object):
        server = GGServer()

        mappings = {'id': 'id',
                'name': 'name',
                'description': 'descr'}
        boolean_mappings = {'isSandbox': 'isSandbox'}

        for child in object.childNodes:
            if child.ELEMENT_NODE == child.nodeType:
                if "attribute" == child.nodeName:
                    name = child.getAttribute("name")

                    if name in mappings:
                        setattr(server, mappings[name], self._get_text(child))
                    elif name in boolean_mappings:
                        setattr(server, boolean_mappings[name], 
                                "true" == self._get_text(child))
                    elif "ip" == name:
                        server.ip = self._parse_ip_object(child.childNodes[0])
                    elif "image" == name:
                        server.image = self._parse_serverimage_object(child.childNodes[0])
                    elif "state" == name:
                         for grandchild in child.childNodes[0].childNodes:
                             if "attribute" == grandchild.nodeName:
                                 if "name" == grandchild.getAttribute("name"):
                                     server.state = self._get_text(grandchild)

        return server

    def _parse_password_object(self, object):
        password = GGPassword()

        mappings = {'id': 'id',
                'username': '******',
                'password': '******'}


        for child in object.childNodes:
            if child.ELEMENT_NODE == child.nodeType:
                name = child.getAttribute("name")

                if name in mappings:
                    setattr(password, mappings[name], self._get_text(child))
                elif "server" == name:
                    password.server = self._parse_server_object(child.childNodes[0])

        if not hasattr(password, 'server'):
            password.server = GGServer()

        return password

    def _parse_job_object(self, object):
        job = GGJob()

        fields = ["id", "owner", "attempts"]
        hist_mappings = {"name": "state",
                "note": "note",
                "description": "descr",
                "updatedon": "updatedon"}

        for child in object.childNodes:
            if child.ELEMENT_NODE == child.nodeType:
                if "attribute" == child.nodeName:
                    name = child.getAttribute("name")

                    if name in fields:
                        setattr(job, name, self._get_text(child))
                    elif "command" == name:
                        for grandchild in child.childNodes[0].childNodes:
                            if "attribute" == grandchild.nodeName:
                                if "description" == grandchild.getAttribute("name"):
                                    job.descr = self._get_text(grandchild)
                    elif "history" == name:
                        job_history_objects = child.getElementsByTagName("object")

                        for job_history_obj in job_history_objects:
                            if "job_history" != job_history_obj.getAttribute("name"):
                                continue

                            gg_job_history = GGJobHistory()

                            if "job_history" == job_history_obj.getAttribute("name"):
                                for job_history_child in job_history_obj.childNodes:
                                    if job_history_child.ELEMENT_NODE == job_history_child.nodeType:
                                        name = job_history_child.getAttribute("name")

                                        if name in hist_mappings:
                                            setattr(gg_job_history, hist_mappings[name], 
                                                    self._get_text(job_history_child))
                                        elif "state" == name:
                                            # deeper and deeper
                                            for state_attr in job_history_child.childNodes[0].childNodes:
                                                if "attribute" == state_attr.nodeName:
                                                    #print state_attrs.getAttribute("name")
                                                    name = state_attr.getAttribute("name")
                                                    if name in hist_mappings:
                                                        setattr(gg_job_history, 
                                                                hist_mappings[name],
                                                                self._get_text(state_attr))

                            job.hist.append(gg_job_history)

        return job