Statistics
| Branch: | Tag: | Revision:

root / snf-tools / snf-admin @ 00f87624

History | View | Annotate | Download (20.2 kB)

1 8cdde414 Giorgos Verigakis
#!/usr/bin/env python
2 8cdde414 Giorgos Verigakis
3 8cdde414 Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
4 8cdde414 Giorgos Verigakis
# 
5 8cdde414 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
6 8cdde414 Giorgos Verigakis
# without modification, are permitted provided that the following
7 8cdde414 Giorgos Verigakis
# conditions are met:
8 8cdde414 Giorgos Verigakis
# 
9 8cdde414 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
10 8cdde414 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
11 8cdde414 Giorgos Verigakis
#      disclaimer.
12 8cdde414 Giorgos Verigakis
# 
13 8cdde414 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
14 8cdde414 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
15 8cdde414 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
16 8cdde414 Giorgos Verigakis
#      provided with the distribution.
17 8cdde414 Giorgos Verigakis
# 
18 8cdde414 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 8cdde414 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 8cdde414 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 8cdde414 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 8cdde414 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 8cdde414 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 8cdde414 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 8cdde414 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 8cdde414 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 8cdde414 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 8cdde414 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 8cdde414 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
30 8cdde414 Giorgos Verigakis
# 
31 8cdde414 Giorgos Verigakis
# The views and conclusions contained in the software and
32 8cdde414 Giorgos Verigakis
# documentation are those of the authors and should not be
33 8cdde414 Giorgos Verigakis
# interpreted as representing official policies, either expressed
34 8cdde414 Giorgos Verigakis
# or implied, of GRNET S.A.
35 8cdde414 Giorgos Verigakis
36 8cdde414 Giorgos Verigakis
from django.core.management import setup_environ
37 8cdde414 Giorgos Verigakis
try:
38 8cdde414 Giorgos Verigakis
    from synnefo import settings
39 8cdde414 Giorgos Verigakis
except ImportError:
40 8cdde414 Giorgos Verigakis
    raise Exception("Cannot import settings, make sure PYTHONPATH contains "
41 8cdde414 Giorgos Verigakis
                    "the parent directory of the Synnefo Django project.")
42 8cdde414 Giorgos Verigakis
setup_environ(settings)
43 8cdde414 Giorgos Verigakis
44 8cdde414 Giorgos Verigakis
import inspect
45 8cdde414 Giorgos Verigakis
import sys
46 8cdde414 Giorgos Verigakis
47 8cdde414 Giorgos Verigakis
from collections import defaultdict
48 910f53d9 Giorgos Verigakis
from itertools import product
49 8cdde414 Giorgos Verigakis
from optparse import OptionParser
50 8cdde414 Giorgos Verigakis
from os.path import basename
51 8cdde414 Giorgos Verigakis
52 8cdde414 Giorgos Verigakis
from synnefo.db import models
53 06b7df28 Giorgos Verigakis
from synnefo.invitations.invitations import add_invitation, send_invitation
54 87c1dadc Giorgos Verigakis
from synnefo.logic import backend, users
55 9e98ba3c Giorgos Verigakis
from synnefo.util.dictconfig import dictConfig
56 8cdde414 Giorgos Verigakis
57 8cdde414 Giorgos Verigakis
58 8cdde414 Giorgos Verigakis
def get_user(uid):
59 8cdde414 Giorgos Verigakis
    try:
60 8cdde414 Giorgos Verigakis
        uid = int(uid)
61 8cdde414 Giorgos Verigakis
        return models.SynnefoUser.objects.get(id=uid)
62 8cdde414 Giorgos Verigakis
    except ValueError:
63 8cdde414 Giorgos Verigakis
        return None
64 8cdde414 Giorgos Verigakis
    except models.SynnefoUser.DoesNotExist:
65 8cdde414 Giorgos Verigakis
        return None
66 8cdde414 Giorgos Verigakis
67 8cdde414 Giorgos Verigakis
def print_dict(d, exclude=()):
68 ca8f8081 Giorgos Verigakis
    if not d:
69 ca8f8081 Giorgos Verigakis
        return
70 8cdde414 Giorgos Verigakis
    margin = max(len(key) for key in d) + 1
71 8cdde414 Giorgos Verigakis
72 8cdde414 Giorgos Verigakis
    for key, val in sorted(d.items()):
73 9a60dacb Giorgos Verigakis
        if key in exclude or key.startswith('_'):
74 8cdde414 Giorgos Verigakis
            continue
75 8cdde414 Giorgos Verigakis
        print '%s: %s' % (key.rjust(margin), val)
76 8cdde414 Giorgos Verigakis
77 13d2b7f7 Giorgos Verigakis
def print_item(item):
78 ead18dbc Giorgos Verigakis
    name = getattr(item, 'name', '')
79 ead18dbc Giorgos Verigakis
    print '%d %s' % (item.id, name)
80 13d2b7f7 Giorgos Verigakis
    print_dict(item.__dict__, exclude=('id', 'name'))
81 13d2b7f7 Giorgos Verigakis
82 1d133009 Giorgos Verigakis
def print_items(items, detail=False, keys=None):
83 1d133009 Giorgos Verigakis
    keys = keys or ('id', 'name')
84 8cdde414 Giorgos Verigakis
    for item in items:
85 1d133009 Giorgos Verigakis
        for key in keys:
86 1d133009 Giorgos Verigakis
            print getattr(item, key),
87 1d133009 Giorgos Verigakis
        print
88 1d133009 Giorgos Verigakis
        
89 8cdde414 Giorgos Verigakis
        if detail:
90 1d133009 Giorgos Verigakis
            print_dict(item.__dict__, exclude=keys)
91 8cdde414 Giorgos Verigakis
            print
92 8cdde414 Giorgos Verigakis
93 8cdde414 Giorgos Verigakis
94 8cdde414 Giorgos Verigakis
class Command(object):
95 9a60dacb Giorgos Verigakis
    group = '<group>'
96 9a60dacb Giorgos Verigakis
    name = '<command>'
97 8cdde414 Giorgos Verigakis
    syntax = ''
98 8cdde414 Giorgos Verigakis
    description = ''
99 735f44fe Giorgos Verigakis
    hidden = False
100 8cdde414 Giorgos Verigakis
    
101 9a60dacb Giorgos Verigakis
    def __init__(self, exe, argv):
102 8cdde414 Giorgos Verigakis
        parser = OptionParser()
103 9a60dacb Giorgos Verigakis
        syntax = '%s [options]' % self.syntax if self.syntax else '[options]'
104 9a60dacb Giorgos Verigakis
        parser.usage = '%s %s %s' % (exe, self.name, syntax)
105 9a60dacb Giorgos Verigakis
        parser.description = self.description
106 8cdde414 Giorgos Verigakis
        self.add_options(parser)
107 8cdde414 Giorgos Verigakis
        options, self.args = parser.parse_args(argv)
108 8cdde414 Giorgos Verigakis
        
109 8cdde414 Giorgos Verigakis
        # Add options to self
110 8cdde414 Giorgos Verigakis
        for opt in parser.option_list:
111 8cdde414 Giorgos Verigakis
            key = opt.dest
112 8cdde414 Giorgos Verigakis
            if key:
113 8cdde414 Giorgos Verigakis
                val = getattr(options, key)
114 8cdde414 Giorgos Verigakis
                setattr(self, key, val)
115 8cdde414 Giorgos Verigakis
        
116 8cdde414 Giorgos Verigakis
        self.parser = parser
117 8cdde414 Giorgos Verigakis
    
118 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
119 8cdde414 Giorgos Verigakis
        pass
120 8cdde414 Giorgos Verigakis
    
121 8cdde414 Giorgos Verigakis
    def execute(self):
122 8cdde414 Giorgos Verigakis
        try:
123 8cdde414 Giorgos Verigakis
            self.main(*self.args)
124 8cdde414 Giorgos Verigakis
        except TypeError:
125 8cdde414 Giorgos Verigakis
            self.parser.print_help()
126 8cdde414 Giorgos Verigakis
127 8cdde414 Giorgos Verigakis
128 13d2b7f7 Giorgos Verigakis
# Server commands
129 13d2b7f7 Giorgos Verigakis
130 8cdde414 Giorgos Verigakis
class ListServers(Command):
131 9a60dacb Giorgos Verigakis
    group = 'server'
132 ca8f8081 Giorgos Verigakis
    name = 'list'
133 8cdde414 Giorgos Verigakis
    syntax = '[server id]'
134 8cdde414 Giorgos Verigakis
    description = 'list servers'
135 8cdde414 Giorgos Verigakis
    
136 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
137 8cdde414 Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
138 8cdde414 Giorgos Verigakis
                        default=False, help='also list deleted servers')
139 8cdde414 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
140 8cdde414 Giorgos Verigakis
                        default=False, help='show detailed output')
141 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
142 8cdde414 Giorgos Verigakis
                            help='show servers of user with id UID')
143 8cdde414 Giorgos Verigakis
    
144 8cdde414 Giorgos Verigakis
    def main(self, server_id=None):
145 8cdde414 Giorgos Verigakis
        if server_id:
146 79c33f4e Giorgos Verigakis
            servers = [models.VirtualMachine.objects.get(id=server_id)]
147 79c33f4e Giorgos Verigakis
        else:
148 79c33f4e Giorgos Verigakis
            servers = models.VirtualMachine.objects.order_by('id')
149 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
150 79c33f4e Giorgos Verigakis
                servers = servers.exclude(deleted=True)
151 79c33f4e Giorgos Verigakis
            if self.uid:
152 79c33f4e Giorgos Verigakis
                user = get_user(self.uid)
153 79c33f4e Giorgos Verigakis
                if user:
154 79c33f4e Giorgos Verigakis
                    servers = servers.filter(owner=user)
155 79c33f4e Giorgos Verigakis
                else:
156 79c33f4e Giorgos Verigakis
                    print 'Unknown user id'
157 79c33f4e Giorgos Verigakis
                    return
158 8cdde414 Giorgos Verigakis
        
159 8cdde414 Giorgos Verigakis
        print_items(servers, self.detail)
160 8cdde414 Giorgos Verigakis
161 8cdde414 Giorgos Verigakis
162 13d2b7f7 Giorgos Verigakis
# User commands
163 13d2b7f7 Giorgos Verigakis
164 735f44fe Giorgos Verigakis
class CreateUser(Command):
165 735f44fe Giorgos Verigakis
    group = 'user'
166 735f44fe Giorgos Verigakis
    name = 'create'
167 06b7df28 Giorgos Verigakis
    syntax = '<username> <email>'
168 735f44fe Giorgos Verigakis
    description = 'create a user'
169 735f44fe Giorgos Verigakis
    
170 735f44fe Giorgos Verigakis
    def add_options(self, parser):
171 735f44fe Giorgos Verigakis
        parser.add_option('--realname', dest='realname', metavar='NAME',
172 735f44fe Giorgos Verigakis
                            help='set real name of user')
173 735f44fe Giorgos Verigakis
        parser.add_option('--type', dest='type', metavar='TYPE',
174 735f44fe Giorgos Verigakis
                            help='set user type')
175 735f44fe Giorgos Verigakis
    
176 06b7df28 Giorgos Verigakis
    def main(self, username, email):
177 22f79931 Giorgos Verigakis
        username = username.decode('utf8')
178 06b7df28 Giorgos Verigakis
        realname = self.realname or username
179 06b7df28 Giorgos Verigakis
        type = self.type or 'USER'
180 d1ceb341 Giorgos Verigakis
        types = [x[0] for x in models.SynnefoUser.ACCOUNT_TYPE]
181 d1ceb341 Giorgos Verigakis
        if type not in types:
182 d1ceb341 Giorgos Verigakis
            valid = ', '.join(types)
183 d1ceb341 Giorgos Verigakis
            print 'Invalid type. Must be one of:', valid
184 06b7df28 Giorgos Verigakis
            return
185 735f44fe Giorgos Verigakis
        
186 87c1dadc Giorgos Verigakis
        user = users._register_user(realname, username, email, type)
187 735f44fe Giorgos Verigakis
        print_item(user)
188 735f44fe Giorgos Verigakis
189 735f44fe Giorgos Verigakis
190 06b7df28 Giorgos Verigakis
class InviteUser(Command):
191 06b7df28 Giorgos Verigakis
    group = 'user'
192 06b7df28 Giorgos Verigakis
    name = 'invite'
193 06b7df28 Giorgos Verigakis
    syntax = '<inviter id> <invitee name> <invitee email>'
194 06b7df28 Giorgos Verigakis
    description = 'invite a user'
195 06b7df28 Giorgos Verigakis
    
196 06b7df28 Giorgos Verigakis
    def main(self, inviter_id, name, email):
197 22f79931 Giorgos Verigakis
        name = name.decode('utf8')
198 06b7df28 Giorgos Verigakis
        inviter = get_user(inviter_id)
199 06b7df28 Giorgos Verigakis
        inv = add_invitation(inviter, name, email)
200 06b7df28 Giorgos Verigakis
        send_invitation(inv)
201 735f44fe Giorgos Verigakis
202 735f44fe Giorgos Verigakis
203 8cdde414 Giorgos Verigakis
class ListUsers(Command):
204 9a60dacb Giorgos Verigakis
    group = 'user'
205 ca8f8081 Giorgos Verigakis
    name = 'list'
206 8cdde414 Giorgos Verigakis
    syntax = '[user id]'
207 8cdde414 Giorgos Verigakis
    description = 'list users'
208 8cdde414 Giorgos Verigakis
    
209 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
210 79c33f4e Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
211 79c33f4e Giorgos Verigakis
                        default=False, help='also list deleted users')
212 8cdde414 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
213 8cdde414 Giorgos Verigakis
                        default=False, help='show detailed output')
214 8cdde414 Giorgos Verigakis
    
215 8cdde414 Giorgos Verigakis
    def main(self, user_id=None):
216 8cdde414 Giorgos Verigakis
        if user_id:
217 8cdde414 Giorgos Verigakis
            users = [models.SynnefoUser.objects.get(id=user_id)]
218 8cdde414 Giorgos Verigakis
        else:
219 79c33f4e Giorgos Verigakis
            users = models.SynnefoUser.objects.order_by('id')
220 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
221 79c33f4e Giorgos Verigakis
                users = users.exclude(state='DELETED')
222 1d133009 Giorgos Verigakis
        print_items(users, self.detail, keys=('id', 'name', 'uniq'))
223 8cdde414 Giorgos Verigakis
224 8cdde414 Giorgos Verigakis
225 13d2b7f7 Giorgos Verigakis
class ModifyUser(Command):
226 13d2b7f7 Giorgos Verigakis
    group = 'user'
227 13d2b7f7 Giorgos Verigakis
    name = 'modify'
228 13d2b7f7 Giorgos Verigakis
    syntax = '<user id>'
229 13d2b7f7 Giorgos Verigakis
    description = 'modify a user'
230 13d2b7f7 Giorgos Verigakis
    
231 13d2b7f7 Giorgos Verigakis
    def add_options(self, parser):
232 d1ceb341 Giorgos Verigakis
        types = ', '.join(x[0] for x in models.SynnefoUser.ACCOUNT_TYPE)
233 870c24a0 Giorgos Verigakis
        states = ', '.join(x[0] for x in models.SynnefoUser.ACCOUNT_STATE)
234 d1ceb341 Giorgos Verigakis
        
235 13d2b7f7 Giorgos Verigakis
        parser.add_option('--credit', dest='credit', metavar='VALUE',
236 13d2b7f7 Giorgos Verigakis
                            help='set user credits')
237 13d2b7f7 Giorgos Verigakis
        parser.add_option('--invitations', dest='invitations',
238 13d2b7f7 Giorgos Verigakis
                            metavar='VALUE', help='set max invitations')
239 13d2b7f7 Giorgos Verigakis
        parser.add_option('--realname', dest='realname', metavar='NAME',
240 13d2b7f7 Giorgos Verigakis
                            help='set real name of user')
241 13d2b7f7 Giorgos Verigakis
        parser.add_option('--type', dest='type', metavar='TYPE',
242 d1ceb341 Giorgos Verigakis
                            help='set user type (%s)' % types)
243 870c24a0 Giorgos Verigakis
        parser.add_option('--state', dest='state', metavar='STATE',
244 870c24a0 Giorgos Verigakis
                            help='set user state (%s)' % states)
245 735f44fe Giorgos Verigakis
        parser.add_option('--uniq', dest='uniq', metavar='ID',
246 735f44fe Giorgos Verigakis
                            help='set external unique ID')
247 13d2b7f7 Giorgos Verigakis
        parser.add_option('--username', dest='username', metavar='NAME',
248 13d2b7f7 Giorgos Verigakis
                            help='set username')
249 13d2b7f7 Giorgos Verigakis
    
250 13d2b7f7 Giorgos Verigakis
    def main(self, user_id):
251 13d2b7f7 Giorgos Verigakis
        user = get_user(user_id)
252 13d2b7f7 Giorgos Verigakis
        
253 13d2b7f7 Giorgos Verigakis
        if self.credit:
254 13d2b7f7 Giorgos Verigakis
            user.credit = self.credit
255 13d2b7f7 Giorgos Verigakis
        if self.invitations:
256 13d2b7f7 Giorgos Verigakis
            user.max_invitations = self.invitations
257 13d2b7f7 Giorgos Verigakis
        if self.realname:
258 13d2b7f7 Giorgos Verigakis
            user.realname = self.realname
259 13d2b7f7 Giorgos Verigakis
        if self.type:
260 13d2b7f7 Giorgos Verigakis
            allowed = [x[0] for x in models.SynnefoUser.ACCOUNT_TYPE]
261 13d2b7f7 Giorgos Verigakis
            if self.type not in allowed:
262 d1ceb341 Giorgos Verigakis
                valid = ', '.join(allowed)
263 d1ceb341 Giorgos Verigakis
                print 'Invalid type. Must be one of:', valid
264 13d2b7f7 Giorgos Verigakis
                return
265 13d2b7f7 Giorgos Verigakis
            user.type = self.type
266 870c24a0 Giorgos Verigakis
        if self.state:
267 870c24a0 Giorgos Verigakis
            allowed = [x[0] for x in models.SynnefoUser.ACCOUNT_STATE]
268 870c24a0 Giorgos Verigakis
            if self.state not in allowed:
269 870c24a0 Giorgos Verigakis
                valid = ', '.join(allowed)
270 870c24a0 Giorgos Verigakis
                print 'Invalid state. Must be one of:', valid
271 870c24a0 Giorgos Verigakis
                return
272 870c24a0 Giorgos Verigakis
            user.state = self.state
273 735f44fe Giorgos Verigakis
        if self.uniq:
274 735f44fe Giorgos Verigakis
            user.uniq = self.uniq
275 13d2b7f7 Giorgos Verigakis
        if self.username:
276 13d2b7f7 Giorgos Verigakis
            user.name = self.username
277 13d2b7f7 Giorgos Verigakis
        
278 13d2b7f7 Giorgos Verigakis
        user.save()
279 13d2b7f7 Giorgos Verigakis
        print_item(user)
280 13d2b7f7 Giorgos Verigakis
281 13d2b7f7 Giorgos Verigakis
282 13d2b7f7 Giorgos Verigakis
# Image commands
283 13d2b7f7 Giorgos Verigakis
284 8cdde414 Giorgos Verigakis
class ListImages(Command):
285 9a60dacb Giorgos Verigakis
    group = 'image'
286 ca8f8081 Giorgos Verigakis
    name = 'list'
287 8cdde414 Giorgos Verigakis
    syntax = '[image id]'
288 8cdde414 Giorgos Verigakis
    description = 'list images'
289 8cdde414 Giorgos Verigakis
    
290 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
291 79c33f4e Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
292 79c33f4e Giorgos Verigakis
                        default=False, help='also list deleted images')
293 8cdde414 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
294 8cdde414 Giorgos Verigakis
                        default=False, help='show detailed output')
295 8cdde414 Giorgos Verigakis
    
296 8cdde414 Giorgos Verigakis
    def main(self, image_id=None):
297 8cdde414 Giorgos Verigakis
        if image_id:
298 8cdde414 Giorgos Verigakis
            images = [models.Image.objects.get(id=image_id)]
299 8cdde414 Giorgos Verigakis
        else:
300 8cdde414 Giorgos Verigakis
            images = models.Image.objects.order_by('id')
301 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
302 79c33f4e Giorgos Verigakis
                images = images.exclude(state='DELETED')
303 8cdde414 Giorgos Verigakis
        print_items(images, self.detail)
304 8cdde414 Giorgos Verigakis
305 8cdde414 Giorgos Verigakis
306 8cdde414 Giorgos Verigakis
class RegisterImage(Command):
307 9a60dacb Giorgos Verigakis
    group = 'image'
308 8cdde414 Giorgos Verigakis
    name = 'register'
309 d1ceb341 Giorgos Verigakis
    syntax = '<name> <backend id> <format>'
310 8cdde414 Giorgos Verigakis
    description = 'register an image'
311 8cdde414 Giorgos Verigakis
    
312 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
313 330c7d80 Giorgos Verigakis
        parser.add_option('--meta', dest='meta', action='append',
314 330c7d80 Giorgos Verigakis
                            metavar='KEY=VAL',
315 bb41504d Giorgos Verigakis
                            help='add metadata (can be used multiple times)')
316 ca8f8081 Giorgos Verigakis
        parser.add_option('--public', action='store_true', dest='public',
317 8cdde414 Giorgos Verigakis
                            default=False, help='make image public')
318 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
319 8cdde414 Giorgos Verigakis
                            help='assign image to user with id UID')
320 8cdde414 Giorgos Verigakis
    
321 d1ceb341 Giorgos Verigakis
    def main(self, name, backend_id, format):
322 d1ceb341 Giorgos Verigakis
        formats = [x[0] for x in models.Image.FORMATS]
323 d1ceb341 Giorgos Verigakis
        if format not in formats:
324 d1ceb341 Giorgos Verigakis
            valid = ', '.join(formats)
325 d1ceb341 Giorgos Verigakis
            print 'Invalid format. Must be one of:', valid
326 d1ceb341 Giorgos Verigakis
            return
327 d1ceb341 Giorgos Verigakis
        
328 8cdde414 Giorgos Verigakis
        user = None
329 8cdde414 Giorgos Verigakis
        if self.uid:
330 8cdde414 Giorgos Verigakis
            user = get_user(self.uid)
331 8cdde414 Giorgos Verigakis
            if not user:
332 8cdde414 Giorgos Verigakis
                print 'Unknown user id'
333 8cdde414 Giorgos Verigakis
                return
334 8cdde414 Giorgos Verigakis
        
335 8cdde414 Giorgos Verigakis
        image = models.Image.objects.create(
336 8cdde414 Giorgos Verigakis
            name=name,
337 8cdde414 Giorgos Verigakis
            state='ACTIVE',
338 8cdde414 Giorgos Verigakis
            owner=user,
339 8cdde414 Giorgos Verigakis
            backend_id=backend_id,
340 d1ceb341 Giorgos Verigakis
            format=format,
341 8cdde414 Giorgos Verigakis
            public=self.public)
342 9a60dacb Giorgos Verigakis
        
343 330c7d80 Giorgos Verigakis
        if self.meta:
344 330c7d80 Giorgos Verigakis
            for m in self.meta:
345 330c7d80 Giorgos Verigakis
                key, sep, val = m.partition('=')
346 330c7d80 Giorgos Verigakis
                if key and val:
347 bb41504d Giorgos Verigakis
                    image.metadata.create(meta_key=key, meta_value=val)
348 330c7d80 Giorgos Verigakis
                else:
349 330c7d80 Giorgos Verigakis
                    print 'WARNING: Ignoring meta', m
350 330c7d80 Giorgos Verigakis
        
351 13d2b7f7 Giorgos Verigakis
        print_item(image)
352 8cdde414 Giorgos Verigakis
353 8cdde414 Giorgos Verigakis
354 8cdde414 Giorgos Verigakis
class ModifyImage(Command):
355 9a60dacb Giorgos Verigakis
    group = 'image'
356 8cdde414 Giorgos Verigakis
    name = 'modify'
357 8cdde414 Giorgos Verigakis
    syntax = '<image id>'
358 8cdde414 Giorgos Verigakis
    description = 'modify an image'
359 8cdde414 Giorgos Verigakis
    
360 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
361 d1ceb341 Giorgos Verigakis
        states = ', '.join(x[0] for x in models.Image.IMAGE_STATES)
362 d1ceb341 Giorgos Verigakis
        formats = ', '.join(x[0] for x in models.Image.FORMATS)
363 d1ceb341 Giorgos Verigakis
364 8cdde414 Giorgos Verigakis
        parser.add_option('-b', dest='backend_id', metavar='BACKEND_ID',
365 8cdde414 Giorgos Verigakis
                            help='set image backend id')
366 8cdde414 Giorgos Verigakis
        parser.add_option('-f', dest='format', metavar='FORMAT',
367 d1ceb341 Giorgos Verigakis
                            help='set image format (%s)' % formats)
368 8cdde414 Giorgos Verigakis
        parser.add_option('-n', dest='name', metavar='NAME',
369 8cdde414 Giorgos Verigakis
                            help='set image name')
370 ca8f8081 Giorgos Verigakis
        parser.add_option('--public', action='store_true', dest='public',
371 8cdde414 Giorgos Verigakis
                            default=False, help='make image public')
372 ca8f8081 Giorgos Verigakis
        parser.add_option('--nopublic', action='store_true', dest='private',
373 8cdde414 Giorgos Verigakis
                            default=False, help='make image private')
374 d1ceb341 Giorgos Verigakis
        parser.add_option('-s', dest='state', metavar='STATE', default=False,
375 d1ceb341 Giorgos Verigakis
                            help='set image state (%s)' % states)
376 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
377 8cdde414 Giorgos Verigakis
                            help='assign image to user with id UID')
378 8cdde414 Giorgos Verigakis
    
379 8cdde414 Giorgos Verigakis
    def main(self, image_id):
380 8cdde414 Giorgos Verigakis
        try:
381 8cdde414 Giorgos Verigakis
            image = models.Image.objects.get(id=image_id)
382 8cdde414 Giorgos Verigakis
        except:
383 8cdde414 Giorgos Verigakis
            print 'Image not found'
384 8cdde414 Giorgos Verigakis
            return
385 8cdde414 Giorgos Verigakis
        
386 8cdde414 Giorgos Verigakis
        if self.backend_id:
387 8cdde414 Giorgos Verigakis
            image.backend_id = self.backend_id
388 8cdde414 Giorgos Verigakis
        if self.format:
389 13d2b7f7 Giorgos Verigakis
            allowed = [x[0] for x in models.Image.FORMATS]
390 13d2b7f7 Giorgos Verigakis
            if self.format not in allowed:
391 d1ceb341 Giorgos Verigakis
                valid = ', '.join(allowed)
392 d1ceb341 Giorgos Verigakis
                print 'Invalid format. Must be one of:', valid
393 13d2b7f7 Giorgos Verigakis
                return
394 13d2b7f7 Giorgos Verigakis
            image.format = self.format
395 8cdde414 Giorgos Verigakis
        if self.name:
396 8cdde414 Giorgos Verigakis
            image.name = self.name
397 8cdde414 Giorgos Verigakis
        if self.public:
398 8cdde414 Giorgos Verigakis
            image.public = True
399 8cdde414 Giorgos Verigakis
        if self.private:
400 8cdde414 Giorgos Verigakis
            image.public = False
401 8cdde414 Giorgos Verigakis
        if self.state:
402 13d2b7f7 Giorgos Verigakis
            allowed = [x[0] for x in models.Image.IMAGE_STATES]
403 13d2b7f7 Giorgos Verigakis
            if self.state not in allowed:
404 d1ceb341 Giorgos Verigakis
                valid = ', '.join(allowed)
405 d1ceb341 Giorgos Verigakis
                print 'Invalid state. Must be one of:', valid
406 13d2b7f7 Giorgos Verigakis
                return
407 8cdde414 Giorgos Verigakis
            image.state = self.state
408 8cdde414 Giorgos Verigakis
        if self.uid:
409 8cdde414 Giorgos Verigakis
            image.owner = get_user(self.uid)
410 8cdde414 Giorgos Verigakis
        
411 8cdde414 Giorgos Verigakis
        image.save()
412 13d2b7f7 Giorgos Verigakis
        print_item(image)
413 8cdde414 Giorgos Verigakis
414 8cdde414 Giorgos Verigakis
415 ca8f8081 Giorgos Verigakis
class ModifyImageMeta(Command):
416 9a60dacb Giorgos Verigakis
    group = 'image'
417 ca8f8081 Giorgos Verigakis
    name = 'meta'
418 ca8f8081 Giorgos Verigakis
    syntax = '<image id> [key[=val]]'
419 ca8f8081 Giorgos Verigakis
    description = 'get and manipulate image metadata'
420 ca8f8081 Giorgos Verigakis
    
421 ca8f8081 Giorgos Verigakis
    def main(self, image_id, arg=''):
422 ca8f8081 Giorgos Verigakis
        try:
423 ca8f8081 Giorgos Verigakis
            image = models.Image.objects.get(id=image_id)
424 ca8f8081 Giorgos Verigakis
        except:
425 ca8f8081 Giorgos Verigakis
            print 'Image not found'
426 ca8f8081 Giorgos Verigakis
            return
427 ca8f8081 Giorgos Verigakis
        
428 ca8f8081 Giorgos Verigakis
        key, sep, val = arg.partition('=')
429 ca8f8081 Giorgos Verigakis
        if not sep:
430 ca8f8081 Giorgos Verigakis
            val = None
431 ca8f8081 Giorgos Verigakis
        
432 ca8f8081 Giorgos Verigakis
        if not key:
433 ca8f8081 Giorgos Verigakis
            metadata = {}
434 7cc3c7d9 Giorgos Verigakis
            for meta in image.metadata.order_by('meta_key'):
435 ca8f8081 Giorgos Verigakis
                metadata[meta.meta_key] = meta.meta_value
436 ca8f8081 Giorgos Verigakis
            print_dict(metadata)
437 ca8f8081 Giorgos Verigakis
            return
438 ca8f8081 Giorgos Verigakis
        
439 ca8f8081 Giorgos Verigakis
        try:
440 7cc3c7d9 Giorgos Verigakis
            meta = image.metadata.get(meta_key=key)
441 ca8f8081 Giorgos Verigakis
        except models.ImageMetadata.DoesNotExist:
442 ca8f8081 Giorgos Verigakis
            meta = None
443 ca8f8081 Giorgos Verigakis
        
444 ca8f8081 Giorgos Verigakis
        if val is None:
445 ca8f8081 Giorgos Verigakis
            if meta:
446 ca8f8081 Giorgos Verigakis
                print_dict({key: meta.meta_value})
447 ca8f8081 Giorgos Verigakis
            return
448 ca8f8081 Giorgos Verigakis
        
449 ca8f8081 Giorgos Verigakis
        if val:
450 ca8f8081 Giorgos Verigakis
            if not meta:
451 7cc3c7d9 Giorgos Verigakis
                meta = image.metadata.create(meta_key=key)
452 ca8f8081 Giorgos Verigakis
            meta.meta_value = val
453 ca8f8081 Giorgos Verigakis
            meta.save()
454 ca8f8081 Giorgos Verigakis
        else:
455 ca8f8081 Giorgos Verigakis
            # Delete if val is empty
456 ca8f8081 Giorgos Verigakis
            if meta:
457 ca8f8081 Giorgos Verigakis
                meta.delete()
458 ca8f8081 Giorgos Verigakis
459 ca8f8081 Giorgos Verigakis
460 910f53d9 Giorgos Verigakis
# Flavor commands
461 910f53d9 Giorgos Verigakis
462 910f53d9 Giorgos Verigakis
class CreateFlavor(Command):
463 910f53d9 Giorgos Verigakis
    group = 'flavor'
464 910f53d9 Giorgos Verigakis
    name = 'create'
465 910f53d9 Giorgos Verigakis
    syntax = '<cpu>[,<cpu>,...] <ram>[,<ram>,...] <disk>[,<disk>,...]'
466 910f53d9 Giorgos Verigakis
    description = 'create one or more flavors'
467 910f53d9 Giorgos Verigakis
    
468 910f53d9 Giorgos Verigakis
    def main(self, cpu, ram, disk):
469 910f53d9 Giorgos Verigakis
        cpus = cpu.split(',')
470 910f53d9 Giorgos Verigakis
        rams = ram.split(',')
471 910f53d9 Giorgos Verigakis
        disks = disk.split(',')
472 910f53d9 Giorgos Verigakis
        
473 910f53d9 Giorgos Verigakis
        flavors = []
474 910f53d9 Giorgos Verigakis
        for cpu, ram, disk in product(cpus, rams, disks):
475 910f53d9 Giorgos Verigakis
            try:
476 910f53d9 Giorgos Verigakis
                flavors.append((int(cpu), int(ram), int(disk)))
477 910f53d9 Giorgos Verigakis
            except ValueError:
478 910f53d9 Giorgos Verigakis
                print 'Invalid values'
479 910f53d9 Giorgos Verigakis
                return
480 910f53d9 Giorgos Verigakis
        
481 910f53d9 Giorgos Verigakis
        created = []
482 910f53d9 Giorgos Verigakis
        for cpu, ram, disk in flavors:
483 910f53d9 Giorgos Verigakis
            flavor = models.Flavor.objects.create(cpu=cpu, ram=ram, disk=disk)
484 910f53d9 Giorgos Verigakis
            created.append(flavor)
485 910f53d9 Giorgos Verigakis
        
486 910f53d9 Giorgos Verigakis
        print_items(created, detail=True)
487 910f53d9 Giorgos Verigakis
488 910f53d9 Giorgos Verigakis
489 910f53d9 Giorgos Verigakis
class DeleteFlavor(Command):
490 910f53d9 Giorgos Verigakis
    group = 'flavor'
491 910f53d9 Giorgos Verigakis
    name = 'delete'
492 910f53d9 Giorgos Verigakis
    syntax = '<flavor id> [<flavor id>] [...]'
493 910f53d9 Giorgos Verigakis
    description = 'delete one or more flavors'
494 910f53d9 Giorgos Verigakis
    
495 910f53d9 Giorgos Verigakis
    def main(self, *args):
496 910f53d9 Giorgos Verigakis
        if not args:
497 910f53d9 Giorgos Verigakis
            raise TypeError
498 910f53d9 Giorgos Verigakis
        for flavor_id in args:
499 910f53d9 Giorgos Verigakis
            flavor = models.Flavor.objects.get(id=int(flavor_id))
500 870c24a0 Giorgos Verigakis
            flavor.deleted = True
501 870c24a0 Giorgos Verigakis
            flavor.save()
502 910f53d9 Giorgos Verigakis
503 910f53d9 Giorgos Verigakis
504 910f53d9 Giorgos Verigakis
class ListFlavors(Command):
505 910f53d9 Giorgos Verigakis
    group = 'flavor'
506 910f53d9 Giorgos Verigakis
    name = 'list'
507 910f53d9 Giorgos Verigakis
    syntax = '[flavor id]'
508 910f53d9 Giorgos Verigakis
    description = 'list images'
509 910f53d9 Giorgos Verigakis
    
510 910f53d9 Giorgos Verigakis
    def add_options(self, parser):
511 79c33f4e Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
512 79c33f4e Giorgos Verigakis
                default=False, help='also list deleted flavors')
513 910f53d9 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
514 910f53d9 Giorgos Verigakis
                        default=False, help='show detailed output')
515 910f53d9 Giorgos Verigakis
    
516 910f53d9 Giorgos Verigakis
    def main(self, flavor_id=None):
517 910f53d9 Giorgos Verigakis
        if flavor_id:
518 910f53d9 Giorgos Verigakis
            flavors = [models.Flavor.objects.get(id=flavor_id)]
519 910f53d9 Giorgos Verigakis
        else:
520 79c33f4e Giorgos Verigakis
            flavors = models.Flavor.objects.order_by('id')
521 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
522 79c33f4e Giorgos Verigakis
                flavors = flavors.exclude(deleted=True)
523 910f53d9 Giorgos Verigakis
        print_items(flavors, self.detail)
524 910f53d9 Giorgos Verigakis
525 910f53d9 Giorgos Verigakis
526 87c1dadc Giorgos Verigakis
class ShowStats(Command):
527 87c1dadc Giorgos Verigakis
    group = 'stats'
528 87c1dadc Giorgos Verigakis
    name = None
529 87c1dadc Giorgos Verigakis
    description = 'show statistics'
530 87c1dadc Giorgos Verigakis
531 87c1dadc Giorgos Verigakis
    def main(self):
532 87c1dadc Giorgos Verigakis
        stats = {}
533 87c1dadc Giorgos Verigakis
        stats['Users'] = models.SynnefoUser.objects.count()
534 87c1dadc Giorgos Verigakis
        stats['Images'] = models.Image.objects.exclude(state='DELETED').count()
535 87c1dadc Giorgos Verigakis
        stats['Flavors'] = models.Flavor.objects.count()
536 87c1dadc Giorgos Verigakis
        stats['VMs'] = models.VirtualMachine.objects.filter(deleted=False).count()
537 87c1dadc Giorgos Verigakis
        stats['Networks'] = models.Network.objects.exclude(state='DELETED').count()
538 87c1dadc Giorgos Verigakis
        stats['Invitations'] = models.Invitations.objects.count()
539 87c1dadc Giorgos Verigakis
        
540 87c1dadc Giorgos Verigakis
        stats['Ganeti Instances'] = len(backend.get_ganeti_instances())
541 87c1dadc Giorgos Verigakis
        stats['Ganeti Nodes'] = len(backend.get_ganeti_nodes())
542 87c1dadc Giorgos Verigakis
        stats['Ganeti Jobs'] = len(backend.get_ganeti_jobs())
543 87c1dadc Giorgos Verigakis
        
544 87c1dadc Giorgos Verigakis
        print_dict(stats)
545 87c1dadc Giorgos Verigakis
546 87c1dadc Giorgos Verigakis
547 ead18dbc Giorgos Verigakis
class ListInvitations(Command):
548 ead18dbc Giorgos Verigakis
    group = 'invitation'
549 ead18dbc Giorgos Verigakis
    name = 'list'
550 ead18dbc Giorgos Verigakis
    syntax = '[invitation id]'
551 ead18dbc Giorgos Verigakis
    description = 'list invitations'
552 ead18dbc Giorgos Verigakis
    
553 ead18dbc Giorgos Verigakis
    def main(self, invitation_id=None):
554 ead18dbc Giorgos Verigakis
        if invitation_id:
555 ead18dbc Giorgos Verigakis
            invitations = [models.Invitations.objects.get(id=invitation_id)]
556 ead18dbc Giorgos Verigakis
        else:
557 ead18dbc Giorgos Verigakis
            invitations = models.Invitations.objects.order_by('id')
558 ead18dbc Giorgos Verigakis
        print_items(invitations, detail=True, keys=('id',))
559 ead18dbc Giorgos Verigakis
560 ead18dbc Giorgos Verigakis
561 ead18dbc Giorgos Verigakis
class ResendInviation(Command):
562 ead18dbc Giorgos Verigakis
    group = 'invitation'
563 ead18dbc Giorgos Verigakis
    name = 'resend'
564 ead18dbc Giorgos Verigakis
    syntax = '<invitation id>'
565 ead18dbc Giorgos Verigakis
    description = 'resend an invitation'
566 ead18dbc Giorgos Verigakis
567 ead18dbc Giorgos Verigakis
    def main(self, invitation_id):
568 ead18dbc Giorgos Verigakis
        invitation = models.Invitations.objects.get(id=invitation_id)
569 ead18dbc Giorgos Verigakis
        send_invitation(invitation)
570 ead18dbc Giorgos Verigakis
571 ead18dbc Giorgos Verigakis
572 9a60dacb Giorgos Verigakis
def print_usage(exe, groups, group=None, shortcut=False):
573 9a60dacb Giorgos Verigakis
    nop = Command(exe, [])
574 9a60dacb Giorgos Verigakis
    nop.parser.print_help()
575 9a60dacb Giorgos Verigakis
    if group:
576 9a60dacb Giorgos Verigakis
        groups = {group: groups[group]}
577 8cdde414 Giorgos Verigakis
578 8cdde414 Giorgos Verigakis
    print
579 8cdde414 Giorgos Verigakis
    print 'Commands:'
580 9a60dacb Giorgos Verigakis
    
581 9a60dacb Giorgos Verigakis
    for group, commands in sorted(groups.items()):
582 9a60dacb Giorgos Verigakis
        for command, cls in sorted(commands.items()):
583 735f44fe Giorgos Verigakis
            if cls.hidden:
584 735f44fe Giorgos Verigakis
                continue
585 ead18dbc Giorgos Verigakis
            name = '  %s %s' % (group, command or '')
586 9a60dacb Giorgos Verigakis
            print '%s %s' % (name.ljust(22), cls.description)
587 9a60dacb Giorgos Verigakis
        print
588 8cdde414 Giorgos Verigakis
589 8cdde414 Giorgos Verigakis
590 8cdde414 Giorgos Verigakis
def main():
591 9a60dacb Giorgos Verigakis
    groups = defaultdict(dict)
592 8cdde414 Giorgos Verigakis
    module = sys.modules[__name__]
593 8cdde414 Giorgos Verigakis
    for name, cls in inspect.getmembers(module, inspect.isclass):
594 9a60dacb Giorgos Verigakis
        if not issubclass(cls, Command) or cls == Command:
595 9a60dacb Giorgos Verigakis
            continue
596 9a60dacb Giorgos Verigakis
        groups[cls.group][cls.name] = cls
597 8cdde414 Giorgos Verigakis
    
598 8cdde414 Giorgos Verigakis
    argv = list(sys.argv)
599 8cdde414 Giorgos Verigakis
    exe = basename(argv.pop(0))
600 9a60dacb Giorgos Verigakis
    prefix, sep, suffix = exe.partition('-')
601 9a60dacb Giorgos Verigakis
    if sep and prefix == 'snf' and suffix in groups:
602 9a60dacb Giorgos Verigakis
        # Allow shortcut aliases like snf-image, snf-server, etc
603 9a60dacb Giorgos Verigakis
        group = suffix
604 8cdde414 Giorgos Verigakis
    else:
605 9a60dacb Giorgos Verigakis
        group = argv.pop(0) if argv else None
606 9a60dacb Giorgos Verigakis
        if group in groups:
607 9a60dacb Giorgos Verigakis
            exe = '%s %s' % (exe, group)
608 9a60dacb Giorgos Verigakis
        else:
609 9a60dacb Giorgos Verigakis
            exe = '%s <group>' % exe
610 9a60dacb Giorgos Verigakis
            group = None
611 8cdde414 Giorgos Verigakis
    
612 8cdde414 Giorgos Verigakis
    command = argv.pop(0) if argv else None
613 8cdde414 Giorgos Verigakis
    
614 9a60dacb Giorgos Verigakis
    if group not in groups or command not in groups[group]:
615 9a60dacb Giorgos Verigakis
        print_usage(exe, groups, group)
616 8cdde414 Giorgos Verigakis
        sys.exit(1)
617 8cdde414 Giorgos Verigakis
    
618 9a60dacb Giorgos Verigakis
    cls = groups[group][command]
619 9a60dacb Giorgos Verigakis
    cmd = cls(exe, argv)
620 8cdde414 Giorgos Verigakis
    cmd.execute()
621 8cdde414 Giorgos Verigakis
622 8cdde414 Giorgos Verigakis
623 8cdde414 Giorgos Verigakis
if __name__ == '__main__':
624 9e98ba3c Giorgos Verigakis
    dictConfig(settings.SNFADMIN_LOGGING)
625 8cdde414 Giorgos Verigakis
    main()