Example #1
0
    def __manage_power(self, vm_name, action="poweroff"):
        """
        Powers a particual virtual machine on/off forcefully.

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param action: action (poweroff, poweron)
        :type action: str

        """
        try:
            content = self.SESSION.RetrieveContent()
            vm = self.__get_obj(content, [vim.VirtualMachine], vm_name)
            if action.lower() == "poweroff":
                #get down with the vickness
                task = vm.PowerOff()
            else:
                #fire it up
                task = vm.PowerOn()
        except AttributeError as err:
            raise SessionException(
                "Unable to manage power state: '{}'".format(err))
        except ValueError as err:
            raise SessionException(
                "Unable to manage power state: '{}'".format(err))
Example #2
0
    def __api_request(self, method, sub_url, payload=""):
        """
        Sends a HTTP request to the Nagios/Icinga API. This function requires
        a valid HTTP method and a sub-URL (such as /cgi-bin/status.cgi).
        Optionally, you can also specify payload (for POST).
        There are also alias functions available.

        :param method: HTTP request method (GET, POST)
        :type method: str
        :param sub_url: relative path (e.g. /cgi-bin/status.cgi)
        :type sub_url: str
        :param payload: payload for POST requests
        :type payload: str

.. seealso:: __api_get()
.. seealso:: __api_post()
        """
        #send request to API
        self.LOGGER.debug("%s request to URL '%s', payload='%s'",
                          method.upper(), sub_url, payload)
        try:
            if method.lower() not in ["get", "post"]:
                #going home
                raise SessionException(
                    "Illegal method '{}' specified".format(method))

            #execute request
            if method.lower() == "post":
                #POST
                result = self.session.post("{}{}".format(self.url, sub_url),
                                           headers=self.HEADERS,
                                           data=payload,
                                           verify=self.verify)
            else:
                #GET
                result = self.session.get("{}{}".format(self.url, sub_url),
                                          headers=self.HEADERS,
                                          verify=self.verify)
            #this really breaks shit
            #self.LOGGER.debug("HTML output: %s", result.text)
            if "error" in result.text.lower():
                tree = html.fromstring(result.text)
                data = tree.xpath("//div[@class='errorMessage']/text()")
                raise SessionException("CGI error: {}".format(data[0]))
            if result.status_code in [401, 403]:
                raise SessionException("Unauthorized")
            elif result.status_code != 200:
                raise SessionException(
                    "{}: HTTP operation not successful".format(
                        result.status_code))
            else:
                #return result
                if method.lower() == "get":
                    return result.text
                else:
                    return True

        except ValueError as err:
            self.LOGGER.error(err)
            raise
Example #3
0
    def __api_request(self, method, sub_url, payload=""):
        """
        Sends a HTTP request to the Nagios/Icinga API. This function requires
        a valid HTTP method and a sub-URL (such as /cgi-bin/status.cgi).
        Optionally, you can also specify payload (for POST).
        There are also alias functions available.

        :param method: HTTP request method (GET, POST)
        :type method: str
        :param sub_url: relative path (e.g. /cgi-bin/status.cgi)
        :type sub_url: str
        :param payload: payload for POST requests
        :type payload: str
        """
        #send request to API
        try:
            if method.lower() not in ["get", "post"]:
                #going home
                raise SessionException(
                    "Illegal method '{}' specified".format(method))
            self.LOGGER.debug("%s request to %s (payload: %s)", method.upper(),
                              sub_url, payload)

            #execute request
            if method.lower() == "post":
                #POST
                result = self.SESSION.post("{}{}".format(self.URL, sub_url),
                                           headers=self.HEADERS,
                                           data=payload,
                                           verify=self.VERIFY_SSL)
            else:
                #GET
                result = self.SESSION.get("{}{}".format(self.URL, sub_url),
                                          headers=self.HEADERS,
                                          verify=self.VERIFY_SSL)

            if result.status_code == 404:
                raise EmptySetException(
                    "HTTP resource not found: {}".format(sub_url))
            elif result.status_code != 200:
                raise SessionException(
                    "{}: HTTP operation not successful".format(
                        result.status_code))
            else:
                #return result
                self.LOGGER.debug(result.text)
                return result

        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
Example #4
0
    def get_name_by_id(self, object_id, api_object):
        """
        Returns a Foreman object's name by its ID.

        param object_id: Foreman object ID
        type object_id: int
        param api_object: Foreman object type (e.g. host, environment)
        type api_object: str
        """
        valid_objects = [
            "hostgroup", "location", "organization", "environment", "host",
            "user"
        ]
        try:
            if api_object.lower() not in valid_objects:
                #invalid type
                raise ValueError("Unable to lookup name by invalid field"
                                 " type '{}'".format(api_object))
            else:
                #get ID by name
                result_obj = json.loads(
                    self.api_get("/{}s/{}".format(api_object, object_id)))
                if result_obj["id"] == object_id:
                    self.LOGGER.debug("I think I found %s #%s...", api_object,
                                      object_id)
                if api_object.lower() == "user":
                    return "{} {}".format(result_obj["firstname"],
                                          result_obj["lastname"])
                else:
                    return result_obj["name"]
        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
Example #5
0
    def __init__(self, log_level, uri, username, password):
        """
        Constructor, creating the class. It requires specifying a URI and
        a username and password for communicating with the hypervisor.
        The constructor will throw an exception if an invalid libvirt URI
        was specified. After initialization, a connection is established
        automatically.

        :param log_level: log level
        :type log_level: logging
        :param uri: libvirt URI
        :type uri: str
        :param username: API username
        :type username: str
        :param password: corresponding password
        :type password: str
        """
        #set logging
        self.LOGGER.setLevel(log_level)
        #validate and set URI
        if self.validate_uri(uri):
            self.URI = uri
        else:
            raise SessionException("Invalid URI string specified!")
        #set connection details and connect
        self.USERNAME = username
        self.PASSWORD = password
        self.__connect()
Example #6
0
    def restart_vm(self, vm_name, force=False):
        """
        Restarts a particular VM (default: soft reboot using guest tools).

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param force: Flag whether a hard reboot is requested
        :type force: bool
        """
        try:
            target_vm = self.SESSION.lookupByName(vm_name)
            if force:
                #kill it with fire
                target_vm.reboot(1)
            else:
                #killing me softly
                target_vm.reboot(0)
        except libvirt.libvirtError as err:
            if "unsupported flags" in err.message.lower():
                #trying hypervisor default
                target_vm.reboot(0)
                self.LOGGER.error(
                    "Forcing reboot impossible, trying hypervisor default")
            else:
                raise SessionException(
                    "Unable to restart VM: '{}'".format(err))
Example #7
0
    def get_vm_ips(self):
        """
        Returns a list of VMs and their IPs available through the current 
        connection.
        """
        try:
            #get all VMs
            vms = self.SESSION.listDefinedDomains()
            result = []

            #scan _all_ the VMs
            for vm in vms:
                #get VM and lookup hostname
                target_vm = self.SESSION.lookupByName(vm)
                target_hostname = target_vm.hostname()
                #lookup IP
                #TODO: IPv6 only?
                target_ip = socket.gethostbyname(target_hostname)
                result.append({"hostname": target_hostname, "ip": target_ip})
            return result
        except libvirt.libvirtError as err:
            if "not supported by" in err.message.lower():
                raise UnsupportedRequestException(err)
            else:
                raise SessionException(
                    "Unable to get VM IP information: '{}'".format(err))
Example #8
0
    def get_hostparam_id_by_name(self, host, param_name):
        """
        Returns a Foreman host parameter's internal ID by its name.

        :param host: Foreman host object ID
        :type host: int
        :param param_name: host parameter name
        :type param_name: str
        """
        #TODO: Move to get_id_by_name
        try:
            result_obj = json.loads(
                self.api_get("/hosts/{}/parameters".format(host)))
            #TODO: nicer way than looping? numpy?
            #TODO allow/return multiple IDs to reduce overhead?
            for entry in result_obj["results"]:
                if entry["name"].lower() == param_name.lower():
                    self.LOGGER.debug(
                        "Found relevant parameter '%s' with ID #%s",
                        entry["name"], entry["id"])
                    return entry["id"]

        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
Example #9
0
    def get_vm_ips(self, hide_empty=True, ipv6_only=False):
        """
        Returns a list of VMs and their IPs available through the current
        connection.

        :param hide_empty: hide VMs without network information
        :type hide_empty: bool
        :param ipv6_only: use IPv6 addresses only
        :type ipv6_only: bool
        """
        try:
            #get _all_ the VMs
            content = self.SESSION.RetrieveContent()
            #result = {}
            result = []
            #create view cotaining VM objects
            object_view = content.viewManager.CreateContainerView(
                content.rootFolder, [vim.VirtualMachine], True)
            for obj in object_view.view:
                if not hide_empty or obj.summary.guest.ipAddress != None:
                    #try to find the best IP
                    self.LOGGER.debug("Trying to find best IP for VM '%s'",
                                      obj.name)
                    if ipv6_only:
                        is_valid_address = is_ipv6
                        self.LOGGER.debug("Filtering for IPv6")
                    else:
                        is_valid_address = is_ipv4
                        self.LOGGER.debug("Filtering for IPv4")

                    target_ip = obj.summary.guest.ipAddress
                    self.LOGGER.debug("Primary guest address: '%s'", target_ip)

                    if not is_valid_address(target_ip):
                        # other NICs
                        for nic in obj.guest.net:
                            for address in nic.ipConfig.ipAddress:
                                if is_valid_address(address.ipAddress):
                                    target_ip = address.ipAddress
                                    self.LOGGER.debug("NIC address: '%s'",
                                                      target_ip)
                                    break

                            if is_valid_address(address.ipAddress):
                                break

                    self.LOGGER.debug("Set IP address to '%s'", target_ip)

                    #Adding result
                    result.append({
                        "object_name": obj.config.name,
                        "hostname": obj.summary.guest.hostName,
                        "ip": target_ip
                    })
            return result
        except Exception as err:
            self.LOGGER.error("Unable to get VM IP information: '%s'", err)
            raise SessionException(err)
Example #10
0
    def powerstate_vm(self, vm_name):
        """
        Returns the power state of a particular virtual machine.

        :param vm_name: Name of a virtual machine
        :type vm_name: str

        """
        try:
            content = self.SESSION.RetrieveContent()
            vm = self.__get_obj(content, [vim.VirtualMachine], vm_name)
            if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
                return "poweredOn"
            elif vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOff:
                return "poweredOff"
        except AttributeError as err:
            raise SessionException(
                "Unable to get power state: '{}'".format(err))
        except ValueError as err:
            raise SessionException(
                "Unable to get power state: '{}'".format(err))
Example #11
0
    def get_id_by_name(self, name, api_object):
        """
        Returns a Foreman object's internal ID by its name.

        :param name: Foreman object name
        :type name: str
        :param api_object: Foreman object type (e.g. host, environment)
        :type api_object: str
        """
        valid_objects = [
            "hostgroup", "location", "organization", "environment", "host"
        ]
        filter_object = {
            "hostgroup": "title",
            "location": "name",
            "host": "name",
            "organization": "title",
            "environment": "name"
        }
        try:
            if api_object.lower() not in valid_objects:
                #invalid type
                raise ValueError("Unable to lookup name by invalid field"
                                 " type '{}'".format(api_object))
            else:
                #get ID by name
                result_obj = json.loads(self.api_get(
                    "/{}s".format(api_object)))
                #TODO: nicer way than looping? numpy? Direct URL?
                for entry in result_obj["results"]:
                    if entry[
                            filter_object[api_object]].lower() == name.lower():
                        self.LOGGER.debug("%s %s seems to have ID #%s",
                                          api_object, name, entry["id"])
                        return entry["id"]
                #not found
                raise SessionException("Object not found")
        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
Example #12
0
 def __connect(self):
     """This function establishes a connection to the hypervisor."""
     #create weirdo auth dict
     auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE],
             self.retrieve_credentials, None]
     #authenticate
     try:
         self.SESSION = libvirt.openAuth(self.URI, auth, 0)
         if self.SESSION == None:
             raise SessionException(
                 "Unable to establish connection to hypervisor!")
     except libvirt.libvirtError as err:
         raise InvalidCredentialsException("Invalid credentials")
Example #13
0
    def get_host_params(self, host):
        """
        Returns all parameters for a particular host.

        :param host: Forenam host name
        :type host: str
        """
        try:
            result_obj = json.loads(
                self.api_get("/hosts/{}/parameters".format(host)))
            return result_obj["results"]
        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
Example #14
0
 def __connect(self):
     """
     This function establishes a connection to Spacewalk.
     """
     #set api session and key
     try:
         self.api_session = Server(self.url)
         self.api_key = self.api_session.auth.login(self.username,
                                                    self.password)
     except Fault as err:
         if err.faultCode == 2950:
             raise InvalidCredentialsException(
                 "Wrong credentials supplied: '%s'", err.faultString)
         else:
             raise SessionException(
                 "Generic remote communication error: '%s'",
                 err.faultString)
Example #15
0
 def get_vm_hosts(self):
     """
     Returns a list of VMs and their hypervisors available through the
     current connection.
     """
     try:
         #get _all_ the VMs
         content = self.SESSION.RetrieveContent()
         result = {}
         #create view cotaining VM objects
         object_view = content.viewManager.CreateContainerView(
             content.rootFolder, [vim.VirtualMachine], True)
         for obj in object_view.view:
             result[obj.config.name] = {"hypervisor": obj.runtime.host.name}
         return result
     except ValueError as err:
         self.LOGGER.error("Unable to get VM hypervisor information: '%s'",
                           err)
         raise SessionException(err)
Example #16
0
    def __manage_snapshot(self,
                          vm_name,
                          snapshot_title,
                          snapshot_text,
                          action="create"):
        """
        Creates/removes a snapshot for a particular virtual machine.
        This requires specifying a VM, comment title and text.
        There are also two alias functions.

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param snapshot_title: Snapshot title
        :type snapshot_title: str
        :param snapshot_text: Snapshot text
        :type snapshot_text: str
        :param remove_snapshot: Removes a snapshot if set to True
        :type remove_snapshot: bool

        """

        try:
            target_vm = self.SESSION.lookupByName(vm_name)
            if action.lower() == "remove":
                #remove snapshot
                target_snap = target_vm.snapshotLookupByName(snapshot_title, 0)
                return target_snap.delete(0)
            elif action.lower() == "revert":
                #revert snapshot
                target_snap = target_vm.snapshotLookupByName(snapshot_title, 0)
                return target_vm.revertToSnapshot(target_snap)
            else:
                #create snapshot
                snap_xml = """<domainsnapshot><name>{}</name><description>{}
                    "</description></domainsnapshot>""".format(
                    snapshot_title, snapshot_text)
                return target_vm.snapshotCreateXML(snap_xml, 0)
        except libvirt.libvirtError as err:
            raise SessionException("Unable to {} snapshot: '{}'".format(
                action.lower(), err))
Example #17
0
    def restart_vm(self, vm_name, force=False):
        """
        Restarts a particular VM (default: soft reboot using guest tools).

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param force: Flag whether a hard reboot is requested
        :type force: bool
        """
        try:
            #get VM
            content = self.SESSION.RetrieveContent()
            vm = self.__get_obj(content, [vim.VirtualMachine], vm_name)

            if force:
                #kill it with fire
                vm.ResetVM_Task()
            else:
                #killing me softly
                vm.RebootGuest()
        except:
            raise SessionException("Unable to restart VM: '{}'".format(
                sys.exc_info()[0]))
Example #18
0
    def has_snapshot(self, vm_name, snapshot_title):
        """
        Returns whether a particular virtual machine is currently protected
        by a snapshot. This requires specifying a VM name.

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param snapshot_title: Snapshot title
        :type snapshot_title: str
        """
        try:
            #find VM and get all snapshots
            target_vm = self.SESSION.lookupByName(vm_name)
            target_snapshots = target_vm.snapshotListNames(0)
            if snapshot_title in target_snapshots:
                return True
        except libvirt.libvirtError as err:
            if "no domain with name" in err.message.lower():
                #snapshot not found
                raise EmptySetException("No snapshots found")
            else:
                self.LOGGER.error(
                    "Unable to determine snapshot: '{}'".format(err))
                raise SessionException(err)
Example #19
0
    def __api_request(self, method, sub_url, payload="", hits=1337, page=1):
        """
        Sends a HTTP request to the Foreman API. This function requires
        a valid HTTP method and a sub-URL (such as /hosts). Optionally,
        you can also specify payload (for POST, DELETE, PUT) and hits/page
        and a page number (when retrieving data using GET).
        There are also alias functions available.

        :param method: HTTP request method (GET, POST, DELETE, PUT)
        :type method: str
        :param sub_url: relative path within the API tree (e.g. /hosts)
        :type sub_url: str
        :param payload: payload for POST/PUT requests
        :type payload: str
        :param hits: numbers of hits/page for GET requests (must be set sadly)
        :type hits: int
        :param page: number of page/results to display (must be set sadly)
        :type page: int

.. todo:: Find a nicer way to display all hits, we shouldn't use 1337 hits/page

.. seealso:: api_get()
.. seealso:: api_post()
.. seealso:: api_put()
.. seealso:: api_delete()
        """
        #send request to API
        try:
            if method.lower() not in ["get", "post", "delete", "put"]:
                #going home
                raise SessionException(
                    "Illegal method '{}' specified".format(method))

            self.LOGGER.debug("%s request to %s%s (payload: %s)",
                              method.upper(), self.URL, sub_url, str(payload))
            #setting headers
            my_headers = self.HEADERS
            if method.lower() != "get":
                #add special headers for non-GETs
                my_headers["Content-Type"] = "application/json"
                my_headers["Accept"] = "application/json,version=2"

            #send request
            if method.lower() == "put":
                #PUT
                result = self.SESSION.put("{}{}".format(self.URL, sub_url),
                                          data=payload,
                                          headers=my_headers,
                                          verify=self.VERIFY)
            elif method.lower() == "delete":
                #DELETE
                result = self.SESSION.delete("{}{}".format(self.URL, sub_url),
                                             data=payload,
                                             headers=my_headers,
                                             verify=self.VERIFY)
            elif method.lower() == "post":
                #POST
                result = self.SESSION.post("{}{}".format(self.URL, sub_url),
                                           data=payload,
                                           headers=my_headers,
                                           verify=self.VERIFY)
            else:
                #GET
                result = self.SESSION.get("{}{}?per_page={}&page={}".format(
                    self.URL, sub_url, hits, page),
                                          headers=self.HEADERS,
                                          verify=self.VERIFY)
            if "unable to authenticate" in result.text.lower():
                raise InvalidCredentialsException("Unable to authenticate")
            if result.status_code not in [200, 201, 202]:
                raise SessionException(
                    "{}: HTTP operation not successful {}".format(
                        result.status_code, result.text))
            else:
                #return result
                if method.lower() == "get":
                    return result.text
                else:
                    return True

        except ValueError as err:
            self.LOGGER.error(err)
            raise SessionException(err)
            pass
Example #20
0
    def __manage_snapshot(self,
                          vm_name,
                          snapshot_title,
                          snapshot_text,
                          action="create"):
        """
        Creates/removes a snapshot for a particular virtual machine.
        This requires specifying a VM, comment title and text.
        There are also two alias functions.

        :param vm_name: Name of a virtual machine
        :type vm_name: str
        :param snapshot_title: Snapshot title
        :type snapshot_title: str
        :param snapshot_text: Snapshot text
        :type snapshot_text: str
        :param action: action (create, remove)
        :type remove_snapshot: str

        """
        #make sure to quiesce and not dump memory
        #TODO: maybe we should supply an option for this?
        dump_memory = False
        quiesce = True
        try:
            content = self.SESSION.RetrieveContent()
            vm = self.__get_obj(content, [vim.VirtualMachine], vm_name)
            if action.lower() != "create":
                #get _all_ the snapshots
                snapshots = self.__get_snapshots(vm_name)
                for snapshot in snapshots:
                    childs = snapshot.childSnapshotList
                    if snapshot.name == snapshot_title:
                        if action.lower() == "remove":
                            #remove snapshot
                            snapshot.snapshot.RemoveSnapshot_Task(True)
                        else:
                            #revert snapshot
                            snapshot.snapshot.RevertToSnapshot_Task()
                    if childs:
                        #also iterate through childs
                        for child in childs:
                            if child.name == snapshot_title:
                                if action.lower() == "remove":
                                    #remove snapshot
                                    child.snapshot.RemoveSnapshot_Task(True)
                                else:
                                    #revert snapshot
                                    child.snapshot.RevertToSnapshot_Task()
            else:
                #only create snapshot if not already existing
                try:
                    if not self.has_snapshot(vm_name, snapshot_title):
                        task = vm.CreateSnapshot(snapshot_title, snapshot_text,
                                                 dump_memory, quiesce)
                    else:
                        raise SnapshotExistsException(
                            "Snapshot '{}' for VM '{}' already exists!".format(
                                snapshot_title, vm_name))
                except EmptySetException as err:
                    task = vm.CreateSnapshot(snapshot_title, snapshot_text,
                                             dump_memory, quiesce)

        except TypeError as err:
            raise SessionException(
                "Unable to manage snapshot: '{}'".format(err))
        except ValueError as err:
            raise SessionException(
                "Unable to manage snapshot: '{}'".format(err))
        except AttributeError as err:
            raise SessionException(
                "Unable to manage snapshot: '{}'".format(err))