Exemplo n.º 1
0
    def amttool_query_state(self, ip_address, power_pass):
        """Ask for node's power state: 'on' or 'off', via amttool."""
        # Retry the state if it fails because it often fails the first time
        for _ in range(10):
            output = self._issue_amttool_command("info", ip_address,
                                                 power_pass)
            if output:
                break
            # Wait 1 second between retries.  AMT controllers are generally
            # very light and may not be comfortable with more frequent
            # queries.
            sleep(1)

        if not output:
            raise PowerActionError("amttool power querying failed.")

        # Ensure that from this point forward that output is a str.
        output = output.decode("utf-8")

        # Wide awake (S0), or asleep (S1-S4), but not a clean slate that
        # will lead to a fresh boot.
        if "S5" in output:
            return "off"
        for state in ("S0", "S1", "S2", "S3", "S4"):
            if state in output:
                return "on"
        raise PowerActionError("Got unknown power state from node: %s" % state)
Exemplo n.º 2
0
 def cb(response_data):
     parsed_data = json.loads(response_data)
     vms = parsed_data["data"]
     if not vms:
         raise PowerActionError(
             "No VMs returned! Are permissions set correctly?"
         )
     for vm in vms:
         if power_vm_name in (str(vm.get("vmid")), vm.get("name")):
             return vm
     raise PowerActionError("Unable to find virtual machine")
Exemplo n.º 3
0
 def run_process(self, *command):
     """Run SNMP command in subprocess."""
     result = shell.run_command(*command)
     if result.returncode != 0:
         raise PowerActionError(
             "APC Power Driver external process error for command %s: %s" %
             ("".join(command), result.stderr))
     match = re.search(r"INTEGER:\s*([1-2])", result.stdout)
     if match is None:
         raise PowerActionError(
             "APC Power Driver unable to extract outlet power state"
             " from: %s" % result.stdout)
     else:
         return match.group(1)
Exemplo n.º 4
0
 def power_query(self, system_id, context):
     """Power query MSCM node."""
     try:
         # Retreive node power state
         #
         # Example of output from running "show node power <node_id>":
         # "show node power c1n1\r\r\n\r\nCartridge #1\r\n  Node #1\r\n
         # Power State: On\r\n"
         output = self.run_mscm_command(
             "show node power %s" % context["node_id"], **context)
     except PowerConnError as e:
         raise PowerActionError(
             "MSCM Power Driver unable to power query node %s: %s" %
             (context["node_id"], e))
     match = re.search(r"Power State:\s*((O[\w]+|U[\w]+))", output)
     if match is None:
         raise PowerFatalError(
             "MSCM Power Driver unable to extract node power state from: %s"
             % output)
     else:
         power_state = match.group(1)
         if power_state in MSCMState.OFF:
             return "off"
         elif power_state == MSCMState.ON:
             return "on"
Exemplo n.º 5
0
 def _set_outlet_state(self,
                       power_change,
                       outlet_id=None,
                       power_user=None,
                       power_pass=None,
                       power_address=None,
                       **extra):
     """Power DLI outlet ON/OFF."""
     try:
         url = "http://%s:%s@%s/outlet?%s=%s" % (
             power_user,
             power_pass,
             power_address,
             outlet_id,
             power_change,
         )
         # --auth-no-challenge: send Basic HTTP authentication
         # information without first waiting for the server's challenge.
         call_and_check(
             ["wget", "--auth-no-challenge", "-O", "/dev/null", url],
             env=get_env_with_locale(),
         )
     except ExternalProcessError as e:
         raise PowerActionError(
             "Failed to power %s outlet %s: %s" %
             (power_change, outlet_id, e.output_as_unicode))
Exemplo n.º 6
0
 def _power_control_seamicro15k_ipmi(self, ip, username, password,
                                     server_id, power_change):
     """Power on/off SeaMicro node via ipmitool."""
     power_mode = 1 if power_change == 'on' else 6
     try:
         call_and_check([
             'ipmitool',
             '-I',
             'lanplus',
             '-H',
             ip,
             '-U',
             username,
             '-P',
             password,
             'raw',
             '0x2E',
             '1',
             '0x00',
             '0x7d',
             '0xab',
             power_mode,
             '0',
             server_id,
         ])
     except ExternalProcessError as e:
         raise PowerActionError(
             "Failed to power %s %s at %s: %s" %
             (power_change, server_id, ip, e.output_as_unicode))
Exemplo n.º 7
0
 def power_off(self, system_id, context):
     """Power off Wedge."""
     try:
         self.run_wedge_command("/usr/local/bin/wedge_power.sh off",
                                **context)
     except PowerConnError:
         raise PowerActionError("Wedge Power Driver unable to power off")
Exemplo n.º 8
0
 def _get_amt_command(self, ip_address, power_pass):
     """Retrieve AMT command to use, either amttool or wsman
     (if AMT version > 8), for the given system.
     """
     # XXX bug=1331214
     # Check if the AMT ver > 8
     # If so, we need wsman, not amttool
     env = self._get_amt_environment(power_pass)
     process = Popen(
         ('amttool', ip_address, 'info'), stdout=PIPE, stderr=PIPE, env=env)
     stdout, stderr = process.communicate()
     stdout = stdout.decode("utf-8")
     stderr = stderr.decode("utf-8")
     if stdout == "" or stdout.isspace():
         for error, error_info in AMT_ERRORS.items():
             if error in stderr:
                 raise error_info.get(
                     'exception')(error_info.get('message'))
         raise PowerConnError(
             "Unable to retrieve AMT version: %s" % stderr)
     else:
         match = re.search("AMT version:\s*([0-9]+)", stdout)
         if match is None:
             raise PowerActionError(
                 "Unable to extract AMT version from "
                 "amttool output: %s" % stdout)
         else:
             version = match.group(1)
             if int(version) > 8:
                 return 'wsman'
             else:
                 return 'amttool'
Exemplo n.º 9
0
 def _issue_ipmitool_command(self,
                             power_change,
                             power_address=None,
                             power_user=None,
                             power_pass=None,
                             power_hwaddress=None,
                             **extra):
     """Issue ipmitool command for HP Moonshot cartridge."""
     command = ('ipmitool', '-I', 'lanplus', '-H', power_address, '-U',
                power_user, '-P', power_pass) + tuple(
                    power_hwaddress.split())
     if power_change == 'pxe':
         command += ('chassis', 'bootdev', 'pxe')
     else:
         command += ('power', power_change)
     try:
         stdout = call_and_check(command, env=select_c_utf8_locale())
         stdout = stdout.decode('utf-8')
     except ExternalProcessError as e:
         raise PowerActionError(
             "Failed to execute %s for cartridge %s at %s: %s" %
             (command, power_hwaddress, power_address, e.output_as_unicode))
     else:
         # Return output if power query
         if power_change == 'status':
             match = re.search(r'\b(on|off)\b$', stdout)
             return stdout if match is None else match.group(0)
Exemplo n.º 10
0
class TestGetErrorMessage(MAASTestCase):

    scenarios = [
        ('auth', dict(
            exception=PowerAuthError('auth'),
            message="Could not authenticate to node's BMC: auth",
            )),
        ('conn', dict(
            exception=PowerConnError('conn'),
            message="Could not contact node's BMC: conn",
            )),
        ('setting', dict(
            exception=PowerSettingError('setting'),
            message="Missing or invalid power setting: setting",
            )),
        ('tool', dict(
            exception=PowerToolError('tool'),
            message="Missing power tool: tool",
            )),
        ('action', dict(
            exception=PowerActionError('action'),
            message="Failed to complete power action: action",
            )),
        ('unknown', dict(
            exception=PowerError('unknown error'),
            message="Failed talking to node's BMC: unknown error",
            )),
    ]

    def test_return_msg(self):
        self.assertEqual(self.message, get_error_message(self.exception))
Exemplo n.º 11
0
 def _power_control_seamicro15k_ipmi(self, ip, username, password,
                                     server_id, power_change):
     """Power on/off SeaMicro node via ipmitool."""
     power_mode = 1 if power_change == "on" else 6
     try:
         call_and_check([
             "ipmitool",
             "-I",
             "lanplus",
             "-H",
             ip,
             "-U",
             username,
             "-P",
             password,
             "-L",
             "OPERATOR",
             "raw",
             "0x2E",
             "1",
             "0x00",
             "0x7d",
             "0xab",
             power_mode,
             "0",
             server_id,
         ])
     except ExternalProcessError as e:
         raise PowerActionError(
             "Failed to power %s %s at %s: %s" %
             (power_change, server_id, ip, e.output_as_unicode))
Exemplo n.º 12
0
        def render_response(response):
            """Render the HTTPS response received."""
            def eb_catch_partial(failure):
                # Twisted is raising PartialDownloadError because the responses
                # do not contain a Content-Length header. Since every response
                # holds the whole body we just take the result.
                failure.trap(PartialDownloadError)
                if int(failure.value.status) == HTTPStatus.OK:
                    return failure.value.response
                else:
                    return failure

            def cb_json_decode(data):
                data = data.decode('utf-8')
                # Only decode non-empty response bodies.
                if data:
                    return json.loads(data)

            def cb_attach_headers(data, headers):
                return data, headers

            # Error out if the response has a status code of 400 or above.
            if response.code >= int(HTTPStatus.BAD_REQUEST):
                raise PowerActionError(
                    "Redfish request failed with response status code:"
                    " %s." % response.code)

            d = readBody(response)
            d.addErrback(eb_catch_partial)
            d.addCallback(cb_json_decode)
            d.addCallback(cb_attach_headers, headers=response.headers)
            return d
Exemplo n.º 13
0
        def render_response(response):
            """Render the HTTPS response received."""
            def eb_catch_partial(failure):
                # Twisted is raising PartialDownloadError because the responses
                # do not contain a Content-Length header. Since every response
                # holds the whole body we just take the result.
                failure.trap(PartialDownloadError)
                if int(failure.value.status) == HTTPStatus.OK:
                    return failure.value.response
                else:
                    return failure

            # Error out if the response has a status code of 400 or above.
            if response.code >= int(HTTPStatus.BAD_REQUEST):
                # if there was no trailing slash, retry with a trailing slash
                # because of varying requirements of BMC manufacturers
                if response.code == HTTPStatus.NOT_FOUND and uri[-1] != b"/":
                    d = agent.request(
                        method,
                        uri + b"/",
                        headers=headers,
                        bodyProducer=bodyProducer,
                    )
                else:
                    raise PowerActionError(
                        "Request failed with response status code: "
                        "%s." % response.code)

            d = readBody(response)
            d.addErrback(eb_catch_partial)
            return d
Exemplo n.º 14
0
    def _query_outlet_state(
        self,
        outlet_id=None,
        power_user=None,
        power_pass=None,
        power_address=None,
        **extra
    ):
        """Query DLI outlet power state.

        Sample snippet of query output from DLI:
        ...
        <!--
        function reg() {
        window.open('http://www.digital-loggers.com/reg.html?SN=LPC751740');
        }
        //-->
        </script>
        </head>
        <!-- state=02 lock=00 -->

        <body alink="#0000FF" vlink="#0000FF">
        <FONT FACE="Arial, Helvetica, Sans-Serif">
        ...
        """
        try:
            url = "http://%s:%s@%s/index.htm" % (
                power_user,
                power_pass,
                power_address,
            )
            # --auth-no-challenge: send Basic HTTP authentication
            # information without first waiting for the server's challenge.
            wget_output = call_and_check(
                ["wget", "--auth-no-challenge", "-qO-", url],
                env=get_env_with_locale(),
            )
            wget_output = wget_output.decode("utf-8")
            match = re.search("<!-- state=([0-9a-fA-F]+)", wget_output)
            if match is None:
                raise PowerError(
                    "Unable to extract power state for outlet %s from "
                    "wget output: %s" % (outlet_id, wget_output)
                )
            else:
                state = match.group(1)
                # state is a bitmap of the DLI's oulet states, where bit 0
                # corresponds to oulet 1's power state, bit 1 corresponds to
                # outlet 2's power state, etc., encoded as hexadecimal.
                if (int(state, 16) & (1 << int(outlet_id) - 1)) > 0:
                    return "on"
                else:
                    return "off"
        except ExternalProcessError as e:
            raise PowerActionError(
                "Failed to power query outlet %s: %s"
                % (outlet_id, e.output_as_unicode)
            )
Exemplo n.º 15
0
Arquivo: apc.py Projeto: zeronewb/maas
 def run_process(self, command):
     """Run SNMP command in subprocess."""
     proc = Popen(
         command.split(), stdout=PIPE, stderr=PIPE,
         env=get_env_with_locale())
     stdout, stderr = proc.communicate()
     stdout = stdout.decode("utf-8")
     stderr = stderr.decode("utf-8")
     if proc.returncode != 0:
         raise PowerActionError(
             "APC Power Driver external process error for command %s: %s"
             % (command, stderr))
     match = re.search("INTEGER:\s*([1-2])", stdout)
     if match is None:
         raise PowerActionError(
             "APC Power Driver unable to extract outlet power state"
             " from: %s" % stdout)
     else:
         return match.group(1)
Exemplo n.º 16
0
 def power_off(self, system_id, context):
     """Power off MSCM node."""
     try:
         # Power node off
         self.run_mscm_command(
             "set node power off force %s" % context["node_id"], **context)
     except PowerConnError as e:
         raise PowerActionError(
             "MSCM Power Driver unable to power off node %s: %s" %
             (context["node_id"], e))
Exemplo n.º 17
0
 def power_off(self, system_id, context):
     """Power off MicrosoftOCS blade."""
     try:
         # Power off blade
         self.get("SetBladeOff", context,
                  ["bladeid=%s" % context["blade_id"]])
     except PowerConnError as e:
         raise PowerActionError(
             "MicrosoftOCS Power Driver unable to power off blade_id %s: %s"
             % (context["blade_id"], e))
Exemplo n.º 18
0
        def render_response(response):
            """Render the HTTPS response received."""

            def eb_catch_partial(failure):
                # Twisted is raising PartialDownloadError because the responses
                # do not contain a Content-Length header. Since every response
                # holds the whole body we just take the result.
                failure.trap(PartialDownloadError)
                if int(failure.value.status) == HTTPStatus.OK:
                    return failure.value.response
                else:
                    return failure

            def cb_json_decode(data):
                data = data.decode("utf-8")
                # Only decode non-empty response bodies.
                if data:
                    # occasionally invalid json is returned. provide a clear
                    # error in that case
                    try:
                        return json.loads(data)
                    except ValueError as error:
                        raise PowerActionError(
                            "Redfish request failed from a JSON parse error:"
                            " %s." % error
                        )

            def cb_attach_headers(data, headers):
                return data, headers

            # Error out if the response has a status code of 400 or above.
            if response.code >= int(HTTPStatus.BAD_REQUEST):
                # if there was no trailing slash, retry with a trailing slash
                # because of varying requirements of BMC manufacturers
                if (
                    response.code == HTTPStatus.NOT_FOUND
                    and uri.decode("utf-8")[-1] != "/"
                ):
                    d = agent.request(
                        method,
                        uri + "/".encode("utf-8"),
                        headers=headers,
                        bodyProducer=bodyProducer,
                    )
                else:
                    raise PowerActionError(
                        "Redfish request failed with response status code:"
                        " %s." % response.code
                    )

            d = readBody(response)
            d.addErrback(eb_catch_partial)
            d.addCallback(cb_json_decode)
            d.addCallback(cb_attach_headers, headers=response.headers)
            return d
Exemplo n.º 19
0
Arquivo: hmc.py Projeto: zhangrb/maas
 def power_off(self, system_id, context):
     """Power off HMC lpar."""
     try:
         # Power lpar off
         self.run_hmc_command(
             "chsysstate -r lpar -m %s -o shutdown -n %s --immed" %
             (context['server_name'], context['lpar']), **context)
     except PowerConnError as e:
         raise PowerActionError(
             "HMC Power Driver unable to power off lpar %s: %s" %
             (context['lpar'], e))
Exemplo n.º 20
0
    def process_vms(data):
        extra_headers, response_data = data
        vms = json.loads(response_data)["data"]
        if not vms:
            raise PowerActionError(
                "No VMs returned! Are permissions set correctly?"
            )
        for vm in vms:
            if prefix_filter and not vm["name"].startswith(prefix_filter):
                continue
            # Proxmox doesn't have an easy way to get the MAC address, it
            # includes it with a bunch of other data in the config.
            vm_config_data = yield proxmox._webhook_request(
                b"GET",
                proxmox._get_url(
                    context,
                    f"nodes/{vm['node']}/{vm['type']}/{vm['vmid']}/config",
                ),
                proxmox._make_auth_headers("", {}, extra_headers),
                verify_ssl,
            )
            macs = [
                mac[0] for mac in mac_regex.findall(vm_config_data.decode())
            ]

            system_id = yield create_node(
                macs,
                "amd64",
                "proxmox",
                {"power_vm_name": vm["vmid"], **context},
                domain,
                hostname=vm["name"].replace(" ", "-"),
            )

            # If the system_id is None an error occured when creating the machine.
            # Most likely the error is the node already exists.
            if system_id is None:
                continue

            if vm["status"] != "stopped":
                yield proxmox._webhook_request(
                    b"POST",
                    proxmox._get_url(
                        context,
                        f"nodes/{vm['node']}/{vm['type']}/{vm['vmid']}/"
                        "status/stop",
                    ),
                    proxmox._make_auth_headers(system_id, {}, extra_headers),
                    context.get("power_verify_ssl") == SSL_INSECURE_YES,
                )

            if accept_all:
                yield commission_node(system_id, user)
Exemplo n.º 21
0
 def cb_json_decode(data):
     data = data.decode("utf-8")
     # Only decode non-empty response bodies.
     if data:
         # occasionally invalid json is returned. provide a clear
         # error in that case
         try:
             return json.loads(data)
         except ValueError as error:
             raise PowerActionError(
                 "Redfish request failed from a JSON parse error:"
                 " %s." % error)
Exemplo n.º 22
0
 def wsman_power_off(self, ip_address, power_pass):
     """Power off the node via wsman."""
     # Issue the wsman command to change power state.
     self._issue_wsman_command("off", ip_address, power_pass)
     # Check power state several times.  It usually takes a second or
     # two to get the correct state.
     for _ in range(10):
         if self.wsman_query_state(ip_address, power_pass) == "off":
             return  # Success.  Machine is off.
         else:
             sleep(1)
     raise PowerActionError("Machine is not powering off.  Giving up.")
Exemplo n.º 23
0
 def wsman_power_on(self, ip_address, power_pass, restart=False):
     """Power on the node via wsman."""
     power_command = "restart" if restart else "on"
     self._set_pxe_boot(ip_address, power_pass)
     self._issue_wsman_command(power_command, ip_address, power_pass)
     # Check power state several times.  It usually takes a second or
     # two to get the correct state.
     for _ in range(10):
         if self.wsman_query_state(ip_address, power_pass) == "on":
             return  # Success.  Machine is on.
         sleep(1)
     raise PowerActionError("Machine is not powering on.  Giving up.")
Exemplo n.º 24
0
 def amttool_power_on(self, ip_address, power_pass, amttool_boot_mode):
     """Power on the node via amttool."""
     # Try several times.  Power commands often fail the first time.
     for _ in range(10):
         # Issue the AMT command; amttool will prompt for confirmation.
         self._issue_amttool_command(
             'powerup', ip_address, power_pass,
             amttool_boot_mode=amttool_boot_mode, stdin=b'yes')
         if self.amttool_query_state(ip_address, power_pass) == 'on':
             return
         sleep(1)
     raise PowerActionError("Machine is not powering on.  Giving up.")
Exemplo n.º 25
0
 def amttool_power_off(self, ip_address, power_pass):
     """Power off the node via amttool."""
     # Try several times.  Power commands often fail the first time.
     for _ in range(10):
         if self.amttool_query_state(ip_address, power_pass) == 'off':
             # Success.  Machine is off.
             return
             # Issue the AMT command; amttool will prompt for confirmation.
         self._issue_amttool_command(
             'powerdown', ip_address, power_pass, stdin=b'yes')
         sleep(1)
     raise PowerActionError("Machine is not powering off.  Giving up.")
Exemplo n.º 26
0
 def power_query(self, system_id, context):
     """Power query APC outlet."""
     power_state = self.run_process(
         'snmpget ' + COMMON_ARGS %
         (context['power_address'], context['node_outlet']))
     if power_state == APCState.OFF:
         return 'off'
     elif power_state == APCState.ON:
         return 'on'
     else:
         raise PowerActionError(
             "APC Power Driver retrieved unknown power state: %r" %
             power_state)
Exemplo n.º 27
0
 def _run(
         self, command: tuple, power_pass: str,
         stdin: bytes=None) -> bytes:
     """Run a subprocess with stdin."""
     env = self._get_amt_environment(power_pass)
     process = Popen(
         command, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
     stdout, stderr = process.communicate(stdin)
     if process.returncode != 0:
         raise PowerActionError(
             "Failed to run command: %s with error: %s" % (
                 command, stderr.decode("utf-8", "replace")))
     return stdout
Exemplo n.º 28
0
    def wsman_query_state(self, ip_address, power_pass):
        """Ask for node's power state: 'on' or 'off', via wsman."""
        # Retry the state if it fails because it often fails the first time.
        output = None
        for _ in range(10):
            output = self._issue_wsman_command('query', ip_address, power_pass)
            if output is not None and len(output) > 0:
                break
            # Wait 1 second between retries.  AMT controllers are generally
            # very light and may not be comfortable with more frequent
            # queries.
            sleep(1)

        if output is None:
            raise PowerActionError("wsman power querying failed.")
        else:
            state = self.get_power_state(output)
            # There are a LOT of possible power states
            # 1: Other                    9: Power Cycle (Off-Hard)
            # 2: On                       10: Master Bus Reset
            # 3: Sleep - Light            11: Diagnostic Interrupt (NMI)
            # 4: Sleep - Deep             12: Off - Soft Graceful
            # 5: Power Cycle (Off - Soft) 13: Off - Hard Graceful
            # 6: Off - Hard               14: Master Bus Reset Graceful
            # 7: Hibernate (Off - Soft)   15: Power Cycle (Off-Soft Graceful)
            # 8: Off - Soft               16: Power Cycle (Off-Hard Graceful)
            #                             17: Diagnostic Interrupt (INIT)

            # These are all power states that indicate that the system is
            # either ON or will resume function in an ON or Powered Up
            # state (e.g. being power cycled currently)
            if state in (
                    '2', '3', '4', '5', '7', '9', '10', '14', '15', '16'):
                return 'on'
            elif state in ('6', '8', '12', '13'):
                return 'off'
            else:
                raise PowerActionError(
                    "Got unknown power state from node: %s" % state)
Exemplo n.º 29
0
Arquivo: hmc.py Projeto: zhangrb/maas
 def power_on(self, system_id, context):
     """Power on HMC lpar."""
     if self.power_query(system_id, context) in HMCState.ON:
         self.power_off(system_id, context)
     try:
         # Power lpar on
         self.run_hmc_command(
             "chsysstate -r lpar -m %s -o on -n %s --bootstring network-all"
             % (context['server_name'], context['lpar']), **context)
     except PowerConnError as e:
         raise PowerActionError(
             "HMC Power Driver unable to power on lpar %s: %s" %
             (context['lpar'], e))
Exemplo n.º 30
0
 def power_query(self, system_id, context):
     """Power query APC outlet."""
     power_state = self.run_process(
         "snmpget",
         *_get_common_args(context["power_address"],
                           context["node_outlet"]),
     )
     if power_state == APCState.OFF:
         return "off"
     elif power_state == APCState.ON:
         return "on"
     else:
         raise PowerActionError(
             "APC Power Driver retrieved unknown power state: %r" %
             power_state)