This file is indexed.

/usr/lib/python2.7/dist-packages/provisioningserver/drivers/hardware/mscm.py is in python-maas-provisioningserver 1.5.4+bzr2294-0ubuntu1.2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Copyright 2014 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Support for managing nodes via the Moonshot HP iLO Chassis Manager CLI.

This module provides support for interacting with HP Moonshot iLO Chassis
Management (MSCM) CLI via SSH, and for using that support to allow MAAS to
manage systems via iLO.
"""

from __future__ import (
    absolute_import,
    print_function,
    unicode_literals,
    )
str = None

__metaclass__ = type
__all__ = [
    'power_control_mscm',
    'probe_and_enlist_mscm',
]

import re

from paramiko import (
    AutoAddPolicy,
    SSHClient,
    )
import provisioningserver.custom_hardware.utils as utils


cartridge_mapping = {
    'ProLiant Moonshot Cartridge': 'amd64/generic',
    'ProLiant m300 Server Cartridge': 'amd64/generic',
    'ProLiant m350 Server Cartridge': 'amd64/generic',
    'ProLiant m400 Server Cartridge': 'arm64/xgene-uboot',
    'ProLiant m500 Server Cartridge': 'amd64/generic',
    'ProLiant m710 Server Cartridge': 'amd64/generic',
    'ProLiant m800 Server Cartridge': 'armhf/keystone',
    'Default': 'arm64/generic',
}


class MSCM_CLI_API(object):
    """An API for interacting with the Moonshot iLO CM CLI."""

    def __init__(self, host, username, password):
        """MSCM_CLI_API Constructor."""
        self.host = host
        self.username = username
        self.password = password
        self._ssh = SSHClient()
        self._ssh.set_missing_host_key_policy(AutoAddPolicy())

    def _run_cli_command(self, command):
        """Run a single command and return unparsed text from stdout."""
        self._ssh.connect(
            self.host, username=self.username, password=self.password)
        try:
            _, stdout, _ = self._ssh.exec_command(command)
            output = stdout.read()
        finally:
            self._ssh.close()

        return output

    def discover_nodes(self):
        """Discover all available nodes.

        Example of stdout from running "show node list":

        'show node list\r\r\nSlot ID    Proc Manufacturer
        Architecture         Memory Power Health\r\n----
        ----- ---------------------- --------------------
        ------ ----- ------\r\n 01  c1n1  Intel Corporation
        x86 Architecture     32 GB  On    OK \r\n 02  c2n1
        N/A                    No Asset Information \r\n\r\n'

        The regex 'c\d+n\d' is finding the node_id's c1-45n1-8
        """
        node_list = self._run_cli_command("show node list")
        return re.findall(r'c\d+n\d', node_list)

    def get_node_macaddr(self, node_id):
        """Get node MAC address(es).

        Example of stdout from running "show node macaddr <node_id>":

        'show node macaddr c1n1\r\r\nSlot ID    NIC 1 (Switch A)
        NIC 2 (Switch B)  NIC 3 (Switch A)  NIC 4 (Switch B)\r\n
        ---- ----- ----------------- ----------------- -----------------
        -----------------\r\n  1  c1n1  a0:1d:48:b5:04:34 a0:1d:48:b5:04:35
        a0:1d:48:b5:04:36 a0:1d:48:b5:04:37\r\n\r\n\r\n'

        The regex '[\:]'.join(['[0-9A-F]{1,2}'] * 6) is finding
        the MAC Addresses for the given node_id.
        """
        macs = self._run_cli_command("show node macaddr %s" % node_id)
        return re.findall(r':'.join(['[0-9a-f]{2}'] * 6), macs)

    def get_node_arch(self, node_id):
        """Get node architecture.

        Example of stdout from running "show node info <node_id>":

        'show node info c1n1\r\r\n\r\nCartridge #1 \r\n  Type: Compute\r\n
        Manufacturer: HP\r\n  Product Name: ProLiant m500 Server Cartridge\r\n'

        Parsing this retrieves 'ProLiant m500 Server Cartridge'
        """
        node_detail = self._run_cli_command("show node info %s" % node_id)
        cartridge = node_detail.split('Product Name: ')[1].splitlines()[0]
        if cartridge in cartridge_mapping:
            return cartridge_mapping[cartridge]
        else:
            return cartridge_mapping['Default']

    def get_node_power_status(self, node_id):
        """Get power state of node (on/off).

        Example of stdout 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'

        Parsing this retrieves 'On'
        """
        power_state = self._run_cli_command("show node power %s" % node_id)
        return power_state.split('Power State: ')[1].splitlines()[0]

    def power_node_on(self, node_id):
        """Power node on."""
        return self._run_cli_command("set node power on %s" % node_id)

    def power_node_off(self, node_id):
        """Power node off."""
        return self._run_cli_command("set node power off force %s" % node_id)

    def configure_node_boot_m2(self, node_id):
        """Configure HDD boot for node."""
        return self._run_cli_command("set node boot M.2 %s" % node_id)

    def configure_node_bootonce_pxe(self, node_id):
        """Configure PXE boot for node once."""
        return self._run_cli_command("set node bootonce pxe %s" % node_id)


def power_control_mscm(host, username, password, node_id, power_change):
    """Handle calls from the power template for nodes with a power type
    of 'mscm'.
    """
    mscm = MSCM_CLI_API(host, username, password)
    power_status = mscm.get_node_power_status(node_id)

    if power_change == 'off':
        mscm.power_node_off(node_id)
        return

    if power_change != 'on':
        raise AssertionError('Unexpected maas power mode.')

    if power_status == 'On':
        mscm.power_node_off(node_id)

    mscm.configure_node_bootonce_pxe(node_id)
    mscm.power_node_on(node_id)


def probe_and_enlist_mscm(host, username, password):
    """ Extracts all of nodes from mscm, sets all of them to boot via HDD by,
    default, sets them to bootonce via PXE, and then enlists them into MAAS.
    """
    mscm = MSCM_CLI_API(host, username, password)
    nodes = mscm.discover_nodes()
    for node_id in nodes:
        # Set default boot to HDD
        mscm.configure_node_boot_m2(node_id)
        params = {
            'power_address': host,
            'power_user': username,
            'power_pass': password,
            'node_id': node_id,
        }
        arch = mscm.get_node_arch(node_id)
        macs = mscm.get_node_macaddr(node_id)
        utils.create_node(macs, arch, 'mscm', params)