Statistics
| Branch: | Tag: | Revision:

root / tools / snf-admin @ ca8f8081

History | View | Annotate | Download (11 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:
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', '_state'))
80
            print
81

    
82

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

    
121

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

    
152

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

    
170

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

    
188

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

    
216

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

    
263

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

    
308

    
309
def print_categories(exe, categories):
310
    print 'Usage: %s <category> <command>' % exe
311
    print
312
    print 'Categories:'
313
    for category in sorted(categories):
314
        print '  %s' % category
315

    
316
def print_commands(exe, commands):
317
    print 'Usage: %s <command>' % exe
318
    print
319
    print 'Commands:'
320
    for command, cls in sorted(commands.items()):
321
        print '  %s %s' % (command.ljust(10), cls.description)
322

    
323

    
324
def main():
325
    categories = defaultdict(dict)
326
    module = sys.modules[__name__]
327
    for name, cls in inspect.getmembers(module, inspect.isclass):
328
        if issubclass(cls, Command) and cls != Command:
329
            categories[cls.category][cls.name] = cls
330
    
331
    argv = list(sys.argv)
332
    exe = basename(argv.pop(0))
333
    snf, sep, rest = exe.partition('-')
334
    if snf == 'snf' and sep and rest != 'admin':
335
        category = rest
336
        if category not in categories:
337
            print 'Invalid alias'
338
            sys.exit(1)
339
    else:
340
        category = argv.pop(0) if argv else None
341
        if category:
342
            exe = '%s %s' % (exe, category)
343
    
344
    if not category or category not in categories:
345
        print_categories(exe, categories)
346
        sys.exit(1)
347
    
348
    command = argv.pop(0) if argv else None
349
    
350
    if not command or command not in categories[category]:
351
        print_commands(exe, categories[category])
352
        sys.exit(1)
353
    
354
    cls = categories[category][command]
355
    cmd = cls(argv)
356
    cmd.execute()
357

    
358

    
359
if __name__ == '__main__':
360
    main()