This file is indexed.

/usr/lib/python2.7/dist-packages/provisioningserver/power/poweraction.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
# Copyright 2012-2014 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,
    )

str = None

__metaclass__ = type
__all__ = [
    "PowerAction",
    "PowerActionFail",
    "UnknownPowerType",
    ]


import os
import subprocess

from celery.app import app_or_default
from provisioningserver.utils import (
    locate_config,
    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 __init__(self, power_action, err):
        self.power_action = power_action
        self.err = err

    def __str__(self):
        message = "%s failed: %s" % (self.power_action.power_type, self.err)
        is_process_error = isinstance(self.err, subprocess.CalledProcessError)
        if is_process_error and self.err.output:
            # Add error output to the message.
            message += ":\n" + self.err.output.strip()
        return message


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 power-type name, e.g. `ipmi`.

    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."""
        return get_power_templates_dir() or locate_config('templates/power')

    @property
    def config_basedir(self):
        """Directory where power config are stored."""
        # By default, power config lives in the same directory as power
        # templates.  This makes it easy to customize them together.
        return get_power_config_dir() or locate_config('templates/power')

    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(self, error)

    def run_shell(self, commands):
        """Execute raw shell script (as rendered from a template).

        :param commands: String containing shell script.
        :raises: :class:`PowerActionFail`
        """
        # This might need retrying but it could be better to leave that
        # to the individual scripts.
        try:
            output = subprocess.check_output(
                commands, shell=True, stderr=subprocess.STDOUT, close_fds=True)
        except subprocess.CalledProcessError as e:
            raise PowerActionFail(self, e)
        # This output is only examined in tests, execute just ignores it
        return output

    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)