/usr/share/pyshared/provisioningserver/power/poweraction.py is in python-maas-provisioningserver 1.2+bzr1373+dfsg-0ubuntu1~12.04.6.
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 | # Copyright 2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Actions for power-related operations."""
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)
__metaclass__ = type
__all__ = [
"PowerAction",
"PowerActionFail",
"UnknownPowerType",
]
import os
import subprocess
from celery.app import app_or_default
from provisioningserver.utils import ShellTemplate
class UnknownPowerType(Exception):
"""Raised when trying to process an unknown power type."""
class PowerActionFail(Exception):
"""Raised when there's a problem executing a power script."""
def get_power_templates_dir():
"""Get the power-templates directory from the config."""
return app_or_default().conf.POWER_TEMPLATES_DIR
def get_power_config_dir():
"""Get the power-config directory from the config."""
return app_or_default().conf.POWER_CONFIG_DIR
class PowerAction:
"""Actions for power-related operations.
:param power_type: A value from :class:`POWER_TYPE`.
The class is intended to be used in two phases:
1. Instantiation, passing the power_type.
2. .execute(), passing any template parameters required by the template.
"""
def __init__(self, power_type):
self.path = os.path.join(
self.template_basedir, power_type + ".template")
if not os.path.exists(self.path):
raise UnknownPowerType(power_type)
self.power_type = power_type
@property
def template_basedir(self):
"""Directory where power templates are stored."""
power_templates_dir = get_power_templates_dir()
if power_templates_dir is None:
# The power templates are installed into the same location
# as this file, and also live in the same directory as this
# file in the source tree.
return os.path.join(os.path.dirname(__file__), 'templates')
else:
return power_templates_dir
@property
def config_basedir(self):
"""Directory where power config are stored."""
power_config_dir = get_power_config_dir()
if power_config_dir is None:
# The power config files are installed into the same location
# as this file, and also live in the same directory as this
# file in the source tree.
return os.path.join(os.path.dirname(__file__), 'config')
else:
return power_config_dir
def get_template(self):
with open(self.path, "rb") as f:
return ShellTemplate(f.read(), name=self.path)
def get_extra_context(self):
"""Extra context used when rending the power templates."""
return {
'config_dir': self.config_basedir,
}
def render_template(self, template, **kwargs):
try:
kwargs.update(self.get_extra_context())
return template.substitute(kwargs)
except NameError as error:
raise PowerActionFail(*error.args)
def run_shell(self, commands):
"""Execute raw shell script (as rendered from a template).
:param commands: String containing shell script.
:param **kwargs: Keyword arguments are passed on to the template as
substitution values.
:return: Tuple of strings: stdout, stderr.
"""
# This might need retrying but it could be better to leave that
# to the individual scripts.
try:
proc = subprocess.Popen(
commands, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True)
except OSError as e:
raise PowerActionFail(e)
stdout, stderr = proc.communicate()
# TODO: log output on errors
code = proc.returncode
if code != 0:
raise PowerActionFail("%s failed with return code %s" % (
self.power_type, code))
return stdout, stderr
def execute(self, **kwargs):
"""Execute the template.
Any supplied parameters will be passed to the template as substitution
values.
"""
template = self.get_template()
rendered = self.render_template(template, **kwargs)
self.run_shell(rendered)
|