Statistics
| Branch: | Tag: | Revision:

root / tools / snf-admin @ 9a60dacb

History | View | Annotate | Download (11.1 kB)

1
#!/usr/bin/env python
2

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

    
36
from django.core.management import setup_environ
37
try:
38
    from synnefo import settings
39
except ImportError:
40
    raise Exception("Cannot import settings, make sure PYTHONPATH contains "
41
                    "the parent directory of the Synnefo Django project.")
42
setup_environ(settings)
43

    
44
import inspect
45
import logging
46
import sys
47

    
48
from collections import defaultdict
49
from optparse import OptionParser
50
from os.path import basename
51

    
52
from synnefo.db import models
53

    
54

    
55
def get_user(uid):
56
    try:
57
        uid = int(uid)
58
        return models.SynnefoUser.objects.get(id=uid)
59
        servers = servers.filter(owner=user)
60
    except ValueError:
61
        return None
62
    except models.SynnefoUser.DoesNotExist:
63
        return None
64

    
65
def print_dict(d, exclude=()):
66
    if not d:
67
        return
68
    margin = max(len(key) for key in d) + 1
69

    
70
    for key, val in sorted(d.items()):
71
        if key in exclude or key.startswith('_'):
72
            continue
73
        print '%s: %s' % (key.rjust(margin), val)
74

    
75
def print_items(items, detail=False):
76
    for item in items:
77
        print '%d %s' % (item.id, item.name)
78
        if detail:
79
            print_dict(item.__dict__, exclude=('id', 'name'))
80
            print
81

    
82

    
83
class Command(object):
84
    group = '<group>'
85
    name = '<command>'
86
    syntax = ''
87
    description = ''
88
    
89
    def __init__(self, exe, argv):
90
        parser = OptionParser()
91
        syntax = '%s [options]' % self.syntax if self.syntax else '[options]'
92
        parser.usage = '%s %s %s' % (exe, self.name, syntax)
93
        parser.description = self.description
94
        self.add_options(parser)
95
        options, self.args = parser.parse_args(argv)
96
        
97
        # Add options to self
98
        for opt in parser.option_list:
99
            key = opt.dest
100
            if key:
101
                val = getattr(options, key)
102
                setattr(self, key, val)
103
        
104
        ch = logging.StreamHandler()
105
        ch.setFormatter(logging.Formatter('%(message)s'))
106
        logger = logging.getLogger()
107
        logger.addHandler(ch)
108
        level = logging.WARNING
109
        logger.setLevel(level)
110
        
111
        self.parser = parser
112
    
113
    def add_options(self, parser):
114
        pass
115
    
116
    def execute(self):
117
        try:
118
            self.main(*self.args)
119
        except TypeError:
120
            self.parser.print_help()
121

    
122

    
123
class ListServers(Command):
124
    group = 'server'
125
    name = 'list'
126
    syntax = '[server id]'
127
    description = 'list servers'
128
    
129
    def add_options(self, parser):
130
        parser.add_option('-a', action='store_true', dest='show_deleted',
131
                        default=False, help='also list deleted servers')
132
        parser.add_option('-l', action='store_true', dest='detail',
133
                        default=False, help='show detailed output')
134
        parser.add_option('-u', dest='uid', metavar='UID',
135
                            help='show servers of user with id UID')
136
    
137
    def main(self, server_id=None):
138
        servers = models.VirtualMachine.objects.order_by('id')
139
        if server_id:
140
            servers = servers.filter(id=server_id)
141
        if not self.show_deleted:
142
            servers = servers.filter(deleted=False)
143
        if self.uid:
144
            user = get_user(self.uid)
145
            if user:
146
                servers = servers.filter(owner=user)
147
            else:
148
                print 'Unknown user id'
149
                return
150
        
151
        print_items(servers, self.detail)
152

    
153

    
154
class ListUsers(Command):
155
    group = 'user'
156
    name = 'list'
157
    syntax = '[user id]'
158
    description = 'list users'
159
    
160
    def add_options(self, parser):
161
        parser.add_option('-l', action='store_true', dest='detail',
162
                        default=False, help='show detailed output')
163
    
164
    def main(self, user_id=None):
165
        if user_id:
166
            users = [models.SynnefoUser.objects.get(id=user_id)]
167
        else:
168
            users = models.SynnefoUser.objects.order_by('id')
169
        print_items(users, self.detail)
170

    
171

    
172
class ListImages(Command):
173
    group = 'image'
174
    name = 'list'
175
    syntax = '[image id]'
176
    description = 'list images'
177
    
178
    def add_options(self, parser):
179
        parser.add_option('-l', action='store_true', dest='detail',
180
                        default=False, help='show detailed output')
181
    
182
    def main(self, image_id=None):
183
        if image_id:
184
            images = [models.Image.objects.get(id=image_id)]
185
        else:
186
            images = models.Image.objects.order_by('id')
187
        print_items(images, self.detail)
188

    
189

    
190
class RegisterImage(Command):
191
    group = 'image'
192
    name = 'register'
193
    syntax = '<name> <backend id>'
194
    description = 'register an image'
195
    
196
    def add_options(self, parser):
197
        parser.add_option('--public', action='store_true', dest='public',
198
                            default=False, help='make image public')
199
        parser.add_option('-u', dest='uid', metavar='UID',
200
                            help='assign image to user with id UID')
201
    
202
    def main(self, name, backend_id):
203
        user = None
204
        if self.uid:
205
            user = get_user(self.uid)
206
            if not user:
207
                print 'Unknown user id'
208
                return
209
        
210
        image = models.Image.objects.create(
211
            name=name,
212
            state='ACTIVE',
213
            owner=user,
214
            backend_id=backend_id,
215
            public=self.public)
216
        
217
        print_items([image], detail=True)
218

    
219

    
220
class ModifyImage(Command):
221
    group = 'image'
222
    name = 'modify'
223
    syntax = '<image id>'
224
    description = 'modify an image'
225
    
226
    def add_options(self, parser):
227
        parser.add_option('-b', dest='backend_id', metavar='BACKEND_ID',
228
                            help='set image backend id')
229
        parser.add_option('-f', dest='format', metavar='FORMAT',
230
                            help='set image format')
231
        parser.add_option('-n', dest='name', metavar='NAME',
232
                            help='set image name')
233
        parser.add_option('--public', action='store_true', dest='public',
234
                            default=False, help='make image public')
235
        parser.add_option('--nopublic', action='store_true', dest='private',
236
                            default=False, help='make image private')
237
        parser.add_option('-s', dest='state', metavar='STATE',
238
                            default=False, help='set image state')
239
        parser.add_option('-u', dest='uid', metavar='UID',
240
                            help='assign image to user with id UID')
241
    
242
    def main(self, image_id):
243
        try:
244
            image = models.Image.objects.get(id=image_id)
245
        except:
246
            print 'Image not found'
247
            return
248
        
249
        if self.backend_id:
250
            image.backend_id = self.backend_id
251
        if self.format:
252
            image.format = format
253
        if self.name:
254
            image.name = self.name
255
        if self.public:
256
            image.public = True
257
        if self.private:
258
            image.public = False
259
        if self.state:
260
            image.state = self.state
261
        if self.uid:
262
            image.owner = get_user(self.uid)
263
        
264
        image.save()
265

    
266

    
267
class ModifyImageMeta(Command):
268
    group = 'image'
269
    name = 'meta'
270
    syntax = '<image id> [key[=val]]'
271
    description = 'get and manipulate image metadata'
272
    
273
    def main(self, image_id, arg=''):
274
        try:
275
            image = models.Image.objects.get(id=image_id)
276
        except:
277
            print 'Image not found'
278
            return
279
        
280
        key, sep, val = arg.partition('=')
281
        if not sep:
282
            val = None
283
        
284
        if not key:
285
            metadata = {}
286
            for meta in image.imagemetadata_set.order_by('meta_key'):
287
                metadata[meta.meta_key] = meta.meta_value
288
            print_dict(metadata)
289
            return
290
        
291
        try:
292
            meta = image.imagemetadata_set.get(meta_key=key)
293
        except models.ImageMetadata.DoesNotExist:
294
            meta = None
295
        
296
        if val is None:
297
            if meta:
298
                print_dict({key: meta.meta_value})
299
            return
300
        
301
        if val:
302
            if not meta:
303
                meta = image.imagemetadata_set.create(meta_key=key)
304
            meta.meta_value = val
305
            meta.save()
306
        else:
307
            # Delete if val is empty
308
            if meta:
309
                meta.delete()
310

    
311

    
312
def print_usage(exe, groups, group=None, shortcut=False):
313
    nop = Command(exe, [])
314
    nop.parser.print_help()
315
    if group:
316
        groups = {group: groups[group]}
317

    
318
    print
319
    print 'Commands:'
320
    
321
    for group, commands in sorted(groups.items()):
322
        for command, cls in sorted(commands.items()):
323
            name = '  %s %s' % (group, command)
324
            print '%s %s' % (name.ljust(22), cls.description)
325
        print
326

    
327

    
328
def main():
329
    groups = defaultdict(dict)
330
    module = sys.modules[__name__]
331
    for name, cls in inspect.getmembers(module, inspect.isclass):
332
        if not issubclass(cls, Command) or cls == Command:
333
            continue
334
        groups[cls.group][cls.name] = cls
335
    
336
    argv = list(sys.argv)
337
    exe = basename(argv.pop(0))
338
    prefix, sep, suffix = exe.partition('-')
339
    if sep and prefix == 'snf' and suffix in groups:
340
        # Allow shortcut aliases like snf-image, snf-server, etc
341
        group = suffix
342
    else:
343
        group = argv.pop(0) if argv else None
344
        if group in groups:
345
            exe = '%s %s' % (exe, group)
346
        else:
347
            exe = '%s <group>' % exe
348
            group = None
349
    
350
    command = argv.pop(0) if argv else None
351
    
352
    if group not in groups or command not in groups[group]:
353
        print_usage(exe, groups, group)
354
        sys.exit(1)
355
    
356
    cls = groups[group][command]
357
    cmd = cls(exe, argv)
358
    cmd.execute()
359

    
360

    
361
if __name__ == '__main__':
362
    main()