/usr/lib/python3/dist-packages/provisioningserver/drivers/power/msftocs.py is in python3-maas-provisioningserver 2.0.0~beta3+bzr4941-0ubuntu1.
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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""MicrosoftOCS Power Driver."""
__all__ = []
import urllib.error
import urllib.parse
import urllib.request
from lxml.etree import fromstring
from provisioningserver.drivers.power import (
PowerActionError,
PowerConnError,
PowerDriver,
PowerFatalError,
)
from provisioningserver.rpc.utils import (
commission_node,
create_node,
)
from provisioningserver.utils.twisted import synchronous
class MicrosoftOCSState(object):
ON = "ON"
OFF = "OFF"
class MicrosoftOCSPowerDriver(PowerDriver):
name = 'msftocs'
description = "MicrosoftOCS Power Driver."
settings = []
def detect_missing_packages(self):
# uses urllib2 http client - nothing to look for!
return []
def extract_from_response(self, response, element_tag):
"""Extract text from first element with element_tag in response."""
root = fromstring(response)
return root.findtext(
'.//ns:%s' % element_tag,
namespaces={'ns': root.nsmap[None]})
def get(self, command, context, params=None):
"""Dispatch a GET request to a Microsoft OCS chassis."""
if params is None:
params = []
else:
params = [param for param in params if bool(param)]
url = urllib.parse.urljoin(
'http://%s:%d/' % (context['power_address'],
context['power_port']),
command) + '?' + '&'.join(params)
authinfo = urllib.request.HTTPPasswordMgrWithDefaultRealm()
authinfo.add_password(
None, url, context['power_user'], context['power_pass'])
proxy_handler = urllib.request.ProxyHandler({})
auth_handler = urllib.request.HTTPBasicAuthHandler(authinfo)
opener = urllib.request.build_opener(proxy_handler, auth_handler)
urllib.request.install_opener(opener)
try:
response = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
raise PowerConnError(
"Could not make proper connection to Microsoft OCS Chassis."
" HTTP error code: %s" % e.code)
except urllib.error.URLError as e:
raise PowerConnError(
"Could not make proper connection to Microsoft OCS Chassis."
" Server could not be reached: %s" % e.reason)
else:
return response.read()
def set_next_boot_device(self, context, pxe=False,
uefi=False, persistent=False):
"""Set Next Boot Device."""
boot_pxe = '2' if pxe else '3'
boot_uefi = 'true' if uefi else 'false'
boot_persistent = 'true' if persistent else 'false'
params = [
"bladeid=%s" % context['blade_id'], "bootType=%s" % boot_pxe,
"uefi=%s" % boot_uefi, "persistent=%s" % boot_persistent
]
self.get('SetNextBoot', context, params)
def get_blades(self, context):
"""Gets available blades.
Returns dictionary of blade numbers and their corresponding
MAC Addresses.
"""
blades = {}
root = fromstring(self.get('GetChassisInfo', context))
namespace = {'ns': root.nsmap[None]}
blade_collections = root.find(
'.//ns:bladeCollections', namespaces=namespace)
# Iterate over all BladeInfo Elements
for blade_info in blade_collections:
blade_mac_address = blade_info.find(
'.//ns:bladeMacAddress', namespaces=namespace)
macs = []
# Iterate over all NicInfo Elements and add MAC Addresses
for nic_info in blade_mac_address:
macs.append(
nic_info.findtext(
'.//ns:macAddress', namespaces=namespace))
macs = [mac for mac in macs if bool(mac)]
if macs:
# Retrive blade id number
bladeid = blade_info.findtext(
'.//ns:bladeNumber', namespaces=namespace)
# Add MAC Addresses for blade
blades[bladeid] = macs
return blades
def power_on(self, system_id, context):
"""Power on MicrosoftOCS blade."""
if self.power_query(system_id, context) == 'on':
self.power_off(system_id, context)
try:
# Set default (persistent) boot to HDD
self.set_next_boot_device(context, persistent=True)
# Set next boot to PXE
self.set_next_boot_device(context, pxe=True)
# Power on blade
self.get(
'SetBladeOn', context, ["bladeid=%s" % context['blade_id']])
except PowerConnError as e:
raise PowerActionError(
"MicrosoftOCS Power Driver unable to power on blade_id %s: %s"
% (context['blade_id'], e))
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))
def power_query(self, system_id, context):
"""Power query MicrosoftOCS blade."""
try:
power_state = self.extract_from_response(
self.get(
'GetBladeState', context,
["bladeid=%s" % context['blade_id']]), 'bladeState')
except PowerConnError as e:
raise PowerActionError(
"MicrosoftOCS Power Driver unable to power query blade_id %s:"
" %r" % (context['blade_id'], e))
else:
if power_state == MicrosoftOCSState.OFF:
return 'off'
elif power_state == MicrosoftOCSState.ON:
return 'on'
else:
raise PowerFatalError(
"MicrosoftOCS Power Driver retrieved unknown power state"
" %s for blade_id %s" % (
power_state, context['blade_id']))
@synchronous
def probe_and_enlist_msftocs(
user, ip, port, username, password, accept_all=False, domain=None):
""" Extracts all of nodes from msftocs, sets all of them to boot via
HDD by, default, sets them to bootonce via PXE, and then enlists them
into MAAS.
"""
port = int(port) or 8000 # Default Port for MicrosoftOCS Chassis is 8000
msftocs_driver = MicrosoftOCSPowerDriver()
context = {
'power_address': ip,
'power_port': port,
'power_user': username,
'power_pass': password,
}
try:
# if get_blades works, we have access to the system
blades = msftocs_driver.get_blades(context)
except urllib.error.HTTPError as e:
raise PowerFatalError(
"Failed to probe nodes for Microsoft OCS with ip=%s "
"port=%d, username=%s, password=%s. HTTP error code: %s"
% (ip, port, username, password, e.code))
except urllib.error.URLError as e:
raise PowerFatalError(
"Failed to probe nodes for Microsoft OCS with ip=%s "
"port=%d, username=%s, password=%s. "
"Server could not be reached: %s"
% (ip, port, username, password, e.reason))
else:
for blade_id, macs in blades.items():
context['blade_id'] = blade_id
# Set default (persistent) boot to HDD
msftocs_driver.set_next_boot_device(context, persistent=True)
# Set next boot to PXE
msftocs_driver.set_next_boot_device(context, pxe=True)
system_id = create_node(
macs, 'amd64', 'msftocs', context, domain).wait(30)
if accept_all:
commission_node(system_id, user).wait(30)
|