Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / management / common.py @ 5a5a1f65

History | View | Annotate | Download (10 kB)

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

    
34

    
35
import ipaddr
36
from datetime import datetime
37

    
38
from django.utils.timesince import timesince, timeuntil
39

    
40
from django.core.management import CommandError
41
from synnefo.db.models import Backend, VirtualMachine, Network, Flavor
42
from synnefo.api.util import get_image as backend_get_image
43
from synnefo.api.faults import ItemNotFound, BadRequest, OverLimit
44
from django.core.exceptions import FieldError
45

    
46
from synnefo.api.util import validate_network_params
47
from synnefo.settings import (CYCLADES_ASTAKOS_SERVICE_TOKEN as ASTAKOS_TOKEN,
48
                              ASTAKOS_URL)
49
from synnefo.logic.rapi import GanetiApiError, GanetiRapiClient
50
from synnefo.lib import astakos
51

    
52
from synnefo.util.text import uenc
53

    
54
import logging
55
log = logging.getLogger(__name__)
56

    
57

    
58
def format_bool(b):
59
    return 'YES' if b else 'NO'
60

    
61

    
62
def parse_bool(string):
63
    if string == "True":
64
        return True
65
    elif string == "False":
66
        return False
67
    else:
68
        raise Exception("Can not parse string %s to bool" % string)
69

    
70

    
71
def format_date(d):
72
    if not d:
73
        return ''
74

    
75
    if d < datetime.now():
76
        return timesince(d) + ' ago'
77
    else:
78
        return 'in ' + timeuntil(d)
79

    
80

    
81
def format_vm_state(vm):
82
    if vm.operstate == "BUILD":
83
        return "BUILD(" + str(vm.buildpercentage) + "%)"
84
    else:
85
        return vm.operstate
86

    
87

    
88
def validate_network_info(options):
89
    subnet = options['subnet']
90
    gateway = options['gateway']
91
    subnet6 = options['subnet6']
92
    gateway6 = options['gateway6']
93

    
94
    try:
95
        validate_network_params(subnet, gateway)
96
    except (BadRequest, OverLimit) as e:
97
        raise CommandError(e)
98

    
99
    return subnet, gateway, subnet6, gateway6
100

    
101

    
102
def get_backend(backend_id):
103
    try:
104
        backend_id = int(backend_id)
105
        return Backend.objects.get(id=backend_id)
106
    except ValueError:
107
        raise CommandError("Invalid Backend ID: %s" % backend_id)
108
    except Backend.DoesNotExist:
109
        raise CommandError("Backend with ID %s not found in DB. "
110
                           " Use snf-manage backend-list to find"
111
                           " out available backend IDs." % backend_id)
112

    
113

    
114
def get_image(image_id, user_id):
115
    if image_id:
116
        try:
117
            return backend_get_image(image_id, user_id)
118
        except ItemNotFound:
119
            raise CommandError("Image with ID %s not found."
120
                               " Use snf-manage image-list to find"
121
                               " out available image IDs." % image_id)
122
    else:
123
        raise CommandError("image-id is mandatory")
124

    
125

    
126
def get_vm(server_id):
127
    try:
128
        server_id = int(server_id)
129
        return VirtualMachine.objects.get(id=server_id)
130
    except ValueError:
131
        raise CommandError("Invalid server ID: %s", server_id)
132
    except VirtualMachine.DoesNotExist:
133
        raise CommandError("Server with ID %s not found in DB."
134
                           " Use snf-manage server-list to find out"
135
                           " available server IDs." % server_id)
136

    
137

    
138
def get_network(network_id):
139
    try:
140
        network_id = int(network_id)
141
        return Network.objects.get(id=network_id)
142
    except ValueError:
143
        raise CommandError("Invalid network ID: %s", network_id)
144
    except Network.DoesNotExist:
145
        raise CommandError("Network with ID %s not found in DB."
146
                           " Use snf-manage network-list to find out"
147
                           " available network IDs." % network_id)
148

    
149

    
150
def get_flavor(flavor_id):
151
    try:
152
        flavor_id = int(flavor_id)
153
        return Flavor.objects.get(id=flavor_id)
154
    except ValueError:
155
        raise CommandError("Invalid flavor ID: %s", flavor_id)
156
    except Flavor.DoesNotExist:
157
        raise CommandError("Flavor with ID %s not found in DB."
158
                           " Use snf-manage flavor-list to find out"
159
                           " available flavor IDs." % flavor_id)
160

    
161

    
162
def filter_results(objects, filter_by):
163
    filter_list = filter_by.split(",")
164
    filter_dict = {}
165
    exclude_dict = {}
166

    
167
    def map_field_type(query):
168
        def fix_bool(val):
169
            if val.lower() in ("yes", "true", "t"):
170
                return True
171
            if val.lower() in ("no", "false", "f"):
172
                return False
173
            return val
174

    
175
        if "!=" in query:
176
            key, val = query.split("!=")
177
            exclude_dict[key] = fix_bool(val)
178
            return
179
        OP_MAP = {
180
            ">=": "__gte",
181
            "=>": "__gte",
182
            ">":  "__gt",
183
            "<=": "__lte",
184
            "=<": "__lte",
185
            "<":  "__lt",
186
            "=":  "",
187
        }
188
        for op, new_op in OP_MAP.items():
189
            if op in query:
190
                key, val = query.split(op)
191
                filter_dict[key + new_op] = fix_bool(val)
192
                return
193

    
194
    map(lambda x: map_field_type(x), filter_list)
195

    
196
    try:
197
        objects = objects.filter(**filter_dict)
198
        return objects.exclude(**exclude_dict)
199
    except FieldError as e:
200
        raise CommandError(e)
201
    except Exception as e:
202
        raise CommandError("Can not filter results: %s" % e)
203

    
204

    
205
def check_backend_credentials(clustername, port, username, password):
206
    try:
207
        client = GanetiRapiClient(clustername, port, username, password)
208
        # This command will raise an exception if there is no
209
        # write-access
210
        client.ModifyCluster()
211
    except GanetiApiError as e:
212
        raise CommandError(e)
213

    
214
    info = client.GetInfo()
215
    info_name = info['name']
216
    if info_name != clustername:
217
        raise CommandError("Invalid clustername value. Please use the"
218
                           " Ganeti Cluster name: %s" % info_name)
219

    
220

    
221
def pprint_table(out, table, headers=None, separator=None):
222
    """Print a pretty, aligned string representation of table.
223

224
    Works by finding out the max width of each column and padding to data
225
    to this value.
226
    """
227

    
228
    assert(isinstance(table, (list, tuple))), "Invalid table type"
229
    sep = separator if separator else "  "
230

    
231
    if headers:
232
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
233
        table.insert(0, headers)
234

    
235
    # Find out the max width of each column
236
    widths = [max(map(len, col)) for col in zip(*table)]
237

    
238
    t_length = sum(widths) + len(sep) * (len(widths) - 1)
239
    if headers:
240
        # pretty print the headers
241
        print >> out, sep.join((str(val).rjust(width)
242
                               for val, width in zip(headers, widths)))
243
        print >> out, "-" * t_length
244
        # remove headers
245
        table = table[1:]
246

    
247
    # print the rest table
248
    for row in table:
249
        print >> out, sep.join(uenc(val.rjust(width))
250
                               for val, width in zip(row, widths))
251

    
252

    
253
class UserCache(object):
254
    """uuid<->displayname user 'cache'"""
255

    
256
    user_catalogs_url = ASTAKOS_URL.replace("im/authenticate",
257
                                            "service/api/user_catalogs")
258

    
259
    def __init__(self, split=100):
260
        self.users = {}
261

    
262
        self.split = split
263
        assert(self.split > 0), "split must be positive"
264

    
265
    def fetch_names(self, uuid_list):
266
        l = len(uuid_list)
267

    
268
        start = 0
269
        while start < l:
270
            end = self.split if l > self.split else l
271
            try:
272
                names = \
273
                    astakos.get_displaynames(token=ASTAKOS_TOKEN,
274
                                             url=UserCache.user_catalogs_url,
275
                                             uuids=uuid_list[start:end])
276
                self.users.update(names)
277
            except Exception as e:
278
                log.error("Failed to fetch names: %s",  e)
279

    
280
            start = end
281

    
282
    def get_uuid(self, name):
283
        if not name in self.users:
284
            try:
285
                self.users[name] = \
286
                    astakos.get_user_uuid(token=ASTAKOS_TOKEN,
287
                                          url=UserCache.user_catalogs_url,
288
                                          displayname=name)
289
            except Exception as e:
290
                log.error("Can not get uuid for name %s: %s", name, e)
291
                self.users[name] = name
292

    
293
        return self.users[name]
294

    
295
    def get_name(self, uuid):
296
        """Do the uuid-to-email resolving"""
297

    
298
        if not uuid in self.users:
299
            try:
300
                self.users[uuid] = \
301
                    astakos.get_displayname(token=ASTAKOS_TOKEN,
302
                                            url=UserCache.user_catalogs_url,
303
                                            uuid=uuid)
304
            except Exception as e:
305
                log.error("Can not get display name for uuid %s: %s", uuid, e)
306
                self.users[uuid] = "-"
307

    
308
        return self.users[uuid]
309

    
310

    
311
class Omit(object):
312
    pass