Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / management / common.py @ 8283d6c1

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
import logging
53
log = logging.getLogger(__name__)
54

    
55

    
56
def format_bool(b):
57
    return 'YES' if b else 'NO'
58

    
59

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

    
68

    
69
def format_date(d):
70
    if not d:
71
        return ''
72

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

    
78

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

    
85

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

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

    
97
    return subnet, gateway, subnet6, gateway6
98

    
99

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

    
111

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

    
123

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

    
135

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

    
147

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

    
159

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

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

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

    
192
    map(lambda x: map_field_type(x), filter_list)
193

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

    
202

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

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

    
218

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

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

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

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

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

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

    
245
    # print the rest table
246
    for row in table:
247
        print >> out, sep.join((str(val).rjust(width).encode('utf8')
248
                               for val, width in zip(row, widths)))
249

    
250

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

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

    
257
    def __init__(self, split=100):
258
        self.users = {}
259

    
260
        self.split = split
261
        assert(self.split > 0), "split must be positive"
262

    
263
    def fetch_names(self, uuid_list):
264
        l = len(uuid_list)
265

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

    
278
            start = end
279

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

    
291
        return self.users[name]
292

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

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

    
306
        return self.users[uuid]
307

    
308

    
309
class Omit(object):
310
    pass