utils.py 6.45 KB
Newer Older
1
# Copyright 2011 GRNET S.A. All rights reserved.
2
#
3
4
5
6
7
8
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#   1. Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
9
#
10
11
12
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
13
#
14
15
16
17
18
19
20
21
22
23
24
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
25
#
26
27
28
29
30
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.

# Utility functions
31

32
from synnefo.db.models import VirtualMachine, Network
33
from snf_django.lib.api import faults
34
from django.conf import settings
35
from copy import deepcopy
36

Christos Stavrakakis's avatar
Christos Stavrakakis committed
37

38
39
40
41
42
43
def id_from_instance_name(name):
    """Returns VirtualMachine's Django id, given a ganeti machine name.

    Strips the ganeti prefix atm. Needs a better name!

    """
44
45
46
47
    sname = str(name)
    if not sname.startswith(settings.BACKEND_PREFIX_ID):
        raise VirtualMachine.InvalidBackendIdError(sname)
    ns = sname.replace(settings.BACKEND_PREFIX_ID, "", 1)
48
    if not ns.isdigit():
49
        raise VirtualMachine.InvalidBackendIdError(sname)
50
51
52

    return int(ns)

53
54

def id_to_instance_name(id):
55
    return "%s%s" % (settings.BACKEND_PREFIX_ID, str(id))
56
57


58
def id_from_network_name(name):
59
    """Returns Network's Django id, given a ganeti network name.
60
61
62
63
64
65

    Strips the ganeti prefix atm. Needs a better name!

    """
    if not str(name).startswith(settings.BACKEND_PREFIX_ID):
        raise Network.InvalidBackendIdError(str(name))
66
    ns = str(name).replace(settings.BACKEND_PREFIX_ID + 'net-', "", 1)
67
68
69
70
71
72
    if not ns.isdigit():
        raise Network.InvalidBackendIdError(str(name))

    return int(ns)


73
def id_to_network_name(id):
74
    return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(id))
75
76


77
78
79
80
81
82
83
84
85
86
87
88
89
def id_from_nic_name(name):
    """Returns NIC's Django id, given a Ganeti's NIC name.

    """
    if not str(name).startswith(settings.BACKEND_PREFIX_ID):
        raise ValueError("Invalid NIC name: %s" % name)
    ns = str(name).replace(settings.BACKEND_PREFIX_ID + 'nic-', "", 1)
    if not ns.isdigit():
        raise ValueError("Invalid NIC name: %s" % name)

    return int(ns)


90
def get_rsapi_state(vm):
91
    """Returns the API state for a virtual machine
Kostas Papadimitriou's avatar
Kostas Papadimitriou committed
92

93
94
95
96
97
    The API state for an instance of VirtualMachine is derived as follows:

    * If the deleted flag has been set, it is "DELETED".
    * Otherwise, it is a mapping of the last state reported by Ganeti
      (vm.operstate) through the RSAPI_STATE_FROM_OPER_STATE dictionary.
Kostas Papadimitriou's avatar
Kostas Papadimitriou committed
98

99
      The last state reported by Ganeti is set whenever Ganeti reports
Christos Stavrakakis's avatar
Christos Stavrakakis committed
100
101
102
103
104
105
106
107
108
      successful completion of an operation. If Ganeti says an
      OP_INSTANCE_STARTUP operation succeeded, vm.operstate is set to
      "STARTED".

    * To support any transitional states defined by the API (only REBOOT for
    the time being) this mapping is amended with information reported by Ganeti
    regarding any outstanding operation. If an OP_INSTANCE_STARTUP had
    succeeded previously and an OP_INSTANCE_REBOOT has been reported as in
    progress, the API state is "REBOOT".
109
110

    """
111
    try:
112
        r = VirtualMachine.RSAPI_STATE_FROM_OPER_STATE[vm.operstate]
113
114
    except KeyError:
        return "UNKNOWN"
115
116
117
    # A machine is DELETED if the deleted flag has been set
    if vm.deleted:
        return "DELETED"
118
    # A machine is in REBOOT if an OP_INSTANCE_REBOOT request is in progress
119
    in_reboot = (r == "ACTIVE") and\
Christos Stavrakakis's avatar
Christos Stavrakakis committed
120
                (vm.backendopcode == "OP_INSTANCE_REBOOT") and\
121
                (vm.backendjobstatus in ("queued", "waiting", "running"))
Christos Stavrakakis's avatar
Christos Stavrakakis committed
122
    if in_reboot:
123
        return "REBOOT"
124
125
126
127
128
129
    in_resize = (r == "STOPPED") and\
                (vm.backendopcode == "OP_INSTANCE_MODIFY") and\
                (vm.task == "RESIZE") and \
                (vm.backendjobstatus in ("queued", "waiting", "running"))
    if in_resize:
        return "RESIZE"
130
131
    return r

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
TASK_STATE_FROM_ACTION = {
    "BUILD": "BULDING",
    "START": "STARTING",
    "STOP": "STOPPING",
    "REBOOT": "REBOOTING",
    "DESTROY": "DESTROYING",
    "RESIZE": "RESIZING",
    "CONNECT": "CONNECTING",
    "DISCONNECT": "DISCONNECTING"}


def get_task_state(vm):
    if vm.task is None:
        return ""
    try:
        return TASK_STATE_FROM_ACTION[vm.task]
    except KeyError:
        return "UNKNOWN"


OPCODE_TO_ACTION = {
    "OP_INSTANCE_CREATE": "BUILD",
    "OP_INSTANCE_START": "START",
    "OP_INSTANCE_STOP": "STOP",
    "OP_INSTANCE_REBOOT": "REBOOT",
    "OP_INSTANCE_REMOVE": "DESTROY"}


def get_action_from_opcode(opcode, job_fields):
    if opcode == "OP_INSTANCE_SET_PARAMS":
        nics = job_fields.get("nics")
        beparams = job_fields.get("beparams")
        if nics:
166
167
168
169
170
171
172
173
174
175
            try:
                nic_action = nics[0][0]
                if nic_action == "add":
                    return "CONNECT"
                elif nic_action == "remove":
                    return "DISCONNECT"
                else:
                    return None
            except:
                return None
176
177
178
179
180
181
182
183
        elif beparams:
            return "RESIZE"
        else:
            return None
    else:
        return OPCODE_TO_ACTION.get(opcode, None)


184
185
def hide_pass(kw):
    if 'osparams' in kw and 'img_passwd' in kw['osparams']:
186
        kw1 = deepcopy(kw)
187
188
189
190
        kw1['osparams']['img_passwd'] = 'x' * 8
        return kw1
    else:
        return kw
191
192
193
194
195
196


def check_name_length(name, max_length, message):
    """Check if a string is within acceptable value length"""
    if len(str(name)) > max_length:
        raise faults.BadRequest(message)