This file is indexed.

/usr/lib/python3/dist-packages/provisioningserver/drivers/power/nova.py is in python3-maas-provisioningserver 2.4.0~beta2-6865-gec43e47e6-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
# Copyright 2016 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Nova Power Driver."""

__all__ = []

from importlib import (
    import_module,
    invalidate_caches,
)
import urllib

from provisioningserver.drivers import (
    IP_EXTRACTOR_PATTERNS,
    make_ip_extractor,
    make_setting_field,
    SETTING_SCOPE,
)
from provisioningserver.drivers.power import (
    PowerAuthError,
    PowerDriver,
    PowerError,
    PowerFatalError,
    PowerToolError,
)
from provisioningserver.logger import get_maas_logger


maaslog = get_maas_logger("drivers.power.nova")


class NovaPowerState:
    NOSTATE = 0
    RUNNING = 1
    PAUSED = 3
    SHUTDOWN = 4
    CRASHED = 6
    SUSPENDED = 7


class NovaPowerDriver(PowerDriver):

    name = 'nova'
    description = "OpenStack Nova"
    settings = [
        make_setting_field(
            'nova_id', "Host UUID", required=True,
            scope=SETTING_SCOPE.NODE),
        make_setting_field('os_tenantname', "Tenant name", required=True),
        make_setting_field('os_username', "Username", required=True),
        make_setting_field(
            'os_password', "Password", field_type='password',
            required=True),
        make_setting_field('os_authurl', "Auth URL", required=True),
    ]
    ip_extractor = make_ip_extractor('os_authurl', IP_EXTRACTOR_PATTERNS.URL)

    nova_api = None

    def power_control_nova(
            self, power_change, nova_id=None, os_tenantname=None,
            os_username=None, os_password=None, os_authurl=None,
            **extra):
        """Control power of nova instances."""
        if not self.try_novaapi_import():
            raise PowerToolError("Missing the python3-novaclient package.")
        nova = self.nova_api.Client(2, os_username,
                                    os_password,
                                    os_tenantname,
                                    os_authurl)

        try:
            urllib.request.urlopen(os_authurl)
        except urllib.error.URLError:
            raise PowerError('%s: URL error' % os_authurl)
        try:
            nova.authenticate()
        except self.nova_api.exceptions.Unauthorized:
            raise PowerAuthError('Failed to authenticate with OpenStack')
        try:
            pwr_stateStr = "OS-EXT-STS:power_state"
            tsk_stateStr = "OS-EXT-STS:task_state"
            vm_stateStr = "OS-EXT-STS:vm_state"
            power_state = getattr(nova.servers.get(nova_id), pwr_stateStr)
            task_state = getattr(nova.servers.get(nova_id), tsk_stateStr)
            vm_state = getattr(nova.servers.get(nova_id), vm_stateStr)
        except self.nova_api.exceptions.NotFound:
            raise PowerError('%s: Instance id not found' % nova_id)

        if power_state == NovaPowerState.NOSTATE:
            raise PowerFatalError('%s: Failed to get power state' % nova_id)
        if power_state == NovaPowerState.RUNNING:
            if (power_change == 'off' and task_state != 'powering-off' and
               vm_state != 'stopped'):
                nova.servers.get(nova_id).stop()
            elif power_change == 'query':
                return 'on'
        if power_state == NovaPowerState.SHUTDOWN:
            if (power_change == 'on' and task_state != 'powering-on' and
               vm_state != 'active'):
                nova.servers.get(nova_id).start()
            elif power_change == 'query':
                return 'off'

    def try_novaapi_import(self):
        """Attempt to import the novaclient API. This API is provided by the
        python3-novaclient package; if it doesn't work out, we need to notify
        the user so they can install it.
        """
        invalidate_caches()
        try:
            if self.nova_api is None:
                self.nova_api = import_module('novaclient.client')
        except ImportError:
            return False
        else:
            return True

    def detect_missing_packages(self):
        """Detect missing package python3-novaclient."""
        if not self.try_novaapi_import():
            return ["python3-novaclient"]
        return []

    def power_on(self, system_id, context):
        """Power on nova instance."""
        self.power_control_nova('on', **context)

    def power_off(self, system_id, context):
        """Power off nova instance."""
        self.power_control_nova('off', **context)

    def power_query(self, system_id, context):
        """Power query nova instance."""
        return self.power_control_nova('query', **context)