Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / tools / admin.py @ f70c8d47

History | View | Annotate | Download (19.9 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 c199b9eb Christos Stavrakakis
from synnefo.logic import backend
54 5adfb622 Giorgos Verigakis
from synnefo.plankton.backend import ImageBackend
55 9e98ba3c Giorgos Verigakis
from synnefo.util.dictconfig import dictConfig
56 8cdde414 Giorgos Verigakis
57 8cdde414 Giorgos Verigakis
58 8cdde414 Giorgos Verigakis
def print_dict(d, exclude=()):
59 ca8f8081 Giorgos Verigakis
    if not d:
60 ca8f8081 Giorgos Verigakis
        return
61 8cdde414 Giorgos Verigakis
    margin = max(len(key) for key in d) + 1
62 8cdde414 Giorgos Verigakis
63 8cdde414 Giorgos Verigakis
    for key, val in sorted(d.items()):
64 9a60dacb Giorgos Verigakis
        if key in exclude or key.startswith('_'):
65 8cdde414 Giorgos Verigakis
            continue
66 8cdde414 Giorgos Verigakis
        print '%s: %s' % (key.rjust(margin), val)
67 8cdde414 Giorgos Verigakis
68 13d2b7f7 Giorgos Verigakis
def print_item(item):
69 ead18dbc Giorgos Verigakis
    name = getattr(item, 'name', '')
70 ead18dbc Giorgos Verigakis
    print '%d %s' % (item.id, name)
71 13d2b7f7 Giorgos Verigakis
    print_dict(item.__dict__, exclude=('id', 'name'))
72 13d2b7f7 Giorgos Verigakis
73 1d133009 Giorgos Verigakis
def print_items(items, detail=False, keys=None):
74 1d133009 Giorgos Verigakis
    keys = keys or ('id', 'name')
75 8cdde414 Giorgos Verigakis
    for item in items:
76 1d133009 Giorgos Verigakis
        for key in keys:
77 1d133009 Giorgos Verigakis
            print getattr(item, key),
78 1d133009 Giorgos Verigakis
        print
79 1d133009 Giorgos Verigakis
        
80 8cdde414 Giorgos Verigakis
        if detail:
81 1d133009 Giorgos Verigakis
            print_dict(item.__dict__, exclude=keys)
82 8cdde414 Giorgos Verigakis
            print
83 8cdde414 Giorgos Verigakis
84 8cdde414 Giorgos Verigakis
85 8cdde414 Giorgos Verigakis
class Command(object):
86 9a60dacb Giorgos Verigakis
    group = '<group>'
87 9a60dacb Giorgos Verigakis
    name = '<command>'
88 8cdde414 Giorgos Verigakis
    syntax = ''
89 8cdde414 Giorgos Verigakis
    description = ''
90 735f44fe Giorgos Verigakis
    hidden = False
91 8cdde414 Giorgos Verigakis
    
92 9a60dacb Giorgos Verigakis
    def __init__(self, exe, argv):
93 8cdde414 Giorgos Verigakis
        parser = OptionParser()
94 9a60dacb Giorgos Verigakis
        syntax = '%s [options]' % self.syntax if self.syntax else '[options]'
95 9a60dacb Giorgos Verigakis
        parser.usage = '%s %s %s' % (exe, self.name, syntax)
96 9a60dacb Giorgos Verigakis
        parser.description = self.description
97 8cdde414 Giorgos Verigakis
        self.add_options(parser)
98 8cdde414 Giorgos Verigakis
        options, self.args = parser.parse_args(argv)
99 8cdde414 Giorgos Verigakis
        
100 8cdde414 Giorgos Verigakis
        # Add options to self
101 8cdde414 Giorgos Verigakis
        for opt in parser.option_list:
102 8cdde414 Giorgos Verigakis
            key = opt.dest
103 8cdde414 Giorgos Verigakis
            if key:
104 8cdde414 Giorgos Verigakis
                val = getattr(options, key)
105 8cdde414 Giorgos Verigakis
                setattr(self, key, val)
106 8cdde414 Giorgos Verigakis
        
107 8cdde414 Giorgos Verigakis
        self.parser = parser
108 8cdde414 Giorgos Verigakis
    
109 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
110 8cdde414 Giorgos Verigakis
        pass
111 8cdde414 Giorgos Verigakis
    
112 8cdde414 Giorgos Verigakis
    def execute(self):
113 8cdde414 Giorgos Verigakis
        try:
114 8cdde414 Giorgos Verigakis
            self.main(*self.args)
115 d19e8f77 Giorgos Verigakis
        except TypeError as e:
116 d19e8f77 Giorgos Verigakis
            if e.args and e.args[0].startswith('main()'):
117 d19e8f77 Giorgos Verigakis
                self.parser.print_help()
118 d19e8f77 Giorgos Verigakis
            else:
119 d19e8f77 Giorgos Verigakis
                raise
120 8cdde414 Giorgos Verigakis
121 8cdde414 Giorgos Verigakis
122 13d2b7f7 Giorgos Verigakis
# Server commands
123 13d2b7f7 Giorgos Verigakis
124 8cdde414 Giorgos Verigakis
class ListServers(Command):
125 9a60dacb Giorgos Verigakis
    group = 'server'
126 ca8f8081 Giorgos Verigakis
    name = 'list'
127 8cdde414 Giorgos Verigakis
    syntax = '[server id]'
128 8cdde414 Giorgos Verigakis
    description = 'list servers'
129 8cdde414 Giorgos Verigakis
    
130 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
131 8cdde414 Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
132 8cdde414 Giorgos Verigakis
                        default=False, help='also list deleted servers')
133 8cdde414 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
134 8cdde414 Giorgos Verigakis
                        default=False, help='show detailed output')
135 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
136 8cdde414 Giorgos Verigakis
                            help='show servers of user with id UID')
137 8cdde414 Giorgos Verigakis
    
138 8cdde414 Giorgos Verigakis
    def main(self, server_id=None):
139 8cdde414 Giorgos Verigakis
        if server_id:
140 79c33f4e Giorgos Verigakis
            servers = [models.VirtualMachine.objects.get(id=server_id)]
141 79c33f4e Giorgos Verigakis
        else:
142 79c33f4e Giorgos Verigakis
            servers = models.VirtualMachine.objects.order_by('id')
143 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
144 79c33f4e Giorgos Verigakis
                servers = servers.exclude(deleted=True)
145 79c33f4e Giorgos Verigakis
            if self.uid:
146 244c552b Giorgos Verigakis
                servers = servers.filter(userid=self.uid)
147 8cdde414 Giorgos Verigakis
        
148 8cdde414 Giorgos Verigakis
        print_items(servers, self.detail)
149 8cdde414 Giorgos Verigakis
150 8cdde414 Giorgos Verigakis
151 13d2b7f7 Giorgos Verigakis
# Image commands
152 13d2b7f7 Giorgos Verigakis
153 8cdde414 Giorgos Verigakis
class ListImages(Command):
154 9a60dacb Giorgos Verigakis
    group = 'image'
155 ca8f8081 Giorgos Verigakis
    name = 'list'
156 8cdde414 Giorgos Verigakis
    syntax = '[image id]'
157 8cdde414 Giorgos Verigakis
    description = 'list images'
158 8cdde414 Giorgos Verigakis
    
159 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
160 79c33f4e Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
161 5adfb622 Giorgos Verigakis
                default=False, help='also list deleted images')
162 8cdde414 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
163 5adfb622 Giorgos Verigakis
                default=False, help='show detailed output')
164 5adfb622 Giorgos Verigakis
        parser.add_option('-p', action='store_true', dest='pithos',
165 5adfb622 Giorgos Verigakis
                default=False, help='show images stored in Pithos')
166 5adfb622 Giorgos Verigakis
        parser.add_option('--user', dest='user',
167 921355f8 Giorgos Verigakis
                default=settings.SYSTEM_IMAGES_OWNER,
168 5adfb622 Giorgos Verigakis
                metavar='USER',
169 5adfb622 Giorgos Verigakis
                help='list images accessible to USER')
170 8cdde414 Giorgos Verigakis
    
171 8cdde414 Giorgos Verigakis
    def main(self, image_id=None):
172 5adfb622 Giorgos Verigakis
        if self.pithos:
173 5adfb622 Giorgos Verigakis
            return self.main_pithos(image_id)
174 5adfb622 Giorgos Verigakis
        
175 8cdde414 Giorgos Verigakis
        if image_id:
176 8cdde414 Giorgos Verigakis
            images = [models.Image.objects.get(id=image_id)]
177 8cdde414 Giorgos Verigakis
        else:
178 8cdde414 Giorgos Verigakis
            images = models.Image.objects.order_by('id')
179 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
180 79c33f4e Giorgos Verigakis
                images = images.exclude(state='DELETED')
181 8cdde414 Giorgos Verigakis
        print_items(images, self.detail)
182 5adfb622 Giorgos Verigakis
    
183 5adfb622 Giorgos Verigakis
    def main_pithos(self, image_id=None):
184 5adfb622 Giorgos Verigakis
        backend = ImageBackend(self.user)
185 5adfb622 Giorgos Verigakis
        if image_id:
186 6ef51e9f Giorgos Verigakis
            images = [backend.get_image(image_id)]
187 5adfb622 Giorgos Verigakis
        else:
188 d19e8f77 Giorgos Verigakis
            images = backend.iter_public()
189 5adfb622 Giorgos Verigakis
        
190 5adfb622 Giorgos Verigakis
        for image in images:
191 5adfb622 Giorgos Verigakis
            print image['id'], image['name']
192 5adfb622 Giorgos Verigakis
            if self.detail:
193 5adfb622 Giorgos Verigakis
                print_dict(image, exclude=('id',))
194 5adfb622 Giorgos Verigakis
                print
195 5adfb622 Giorgos Verigakis
        
196 5adfb622 Giorgos Verigakis
        backend.close()
197 8cdde414 Giorgos Verigakis
198 8cdde414 Giorgos Verigakis
199 8cdde414 Giorgos Verigakis
class RegisterImage(Command):
200 9a60dacb Giorgos Verigakis
    group = 'image'
201 8cdde414 Giorgos Verigakis
    name = 'register'
202 921355f8 Giorgos Verigakis
    syntax = '<name> <Backend ID or Pithos URL> <disk format>'
203 8cdde414 Giorgos Verigakis
    description = 'register an image'
204 8cdde414 Giorgos Verigakis
    
205 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
206 330c7d80 Giorgos Verigakis
        parser.add_option('--meta', dest='meta', action='append',
207 921355f8 Giorgos Verigakis
                metavar='KEY=VAL',
208 921355f8 Giorgos Verigakis
                help='add metadata (can be used multiple times)')
209 ca8f8081 Giorgos Verigakis
        parser.add_option('--public', action='store_true', dest='public',
210 921355f8 Giorgos Verigakis
                default=False, help='make image public')
211 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
212 921355f8 Giorgos Verigakis
                help='assign image to user with id UID')
213 8cdde414 Giorgos Verigakis
    
214 d1ceb341 Giorgos Verigakis
    def main(self, name, backend_id, format):
215 921355f8 Giorgos Verigakis
        if backend_id.startswith('pithos://'):
216 921355f8 Giorgos Verigakis
            return self.main_pithos(name, backend_id, format)
217 921355f8 Giorgos Verigakis
        
218 d1ceb341 Giorgos Verigakis
        formats = [x[0] for x in models.Image.FORMATS]
219 d1ceb341 Giorgos Verigakis
        if format not in formats:
220 d1ceb341 Giorgos Verigakis
            valid = ', '.join(formats)
221 d1ceb341 Giorgos Verigakis
            print 'Invalid format. Must be one of:', valid
222 d1ceb341 Giorgos Verigakis
            return
223 d1ceb341 Giorgos Verigakis
        
224 8cdde414 Giorgos Verigakis
        image = models.Image.objects.create(
225 8cdde414 Giorgos Verigakis
            name=name,
226 8cdde414 Giorgos Verigakis
            state='ACTIVE',
227 244c552b Giorgos Verigakis
            owner=self.uid,
228 8cdde414 Giorgos Verigakis
            backend_id=backend_id,
229 d1ceb341 Giorgos Verigakis
            format=format,
230 8cdde414 Giorgos Verigakis
            public=self.public)
231 9a60dacb Giorgos Verigakis
        
232 330c7d80 Giorgos Verigakis
        if self.meta:
233 330c7d80 Giorgos Verigakis
            for m in self.meta:
234 330c7d80 Giorgos Verigakis
                key, sep, val = m.partition('=')
235 330c7d80 Giorgos Verigakis
                if key and val:
236 bb41504d Giorgos Verigakis
                    image.metadata.create(meta_key=key, meta_value=val)
237 330c7d80 Giorgos Verigakis
                else:
238 330c7d80 Giorgos Verigakis
                    print 'WARNING: Ignoring meta', m
239 330c7d80 Giorgos Verigakis
        
240 13d2b7f7 Giorgos Verigakis
        print_item(image)
241 921355f8 Giorgos Verigakis
    
242 921355f8 Giorgos Verigakis
    def main_pithos(self, name, url, disk_format):
243 921355f8 Giorgos Verigakis
        if disk_format not in settings.ALLOWED_DISK_FORMATS:
244 921355f8 Giorgos Verigakis
            print 'Invalid disk format'
245 921355f8 Giorgos Verigakis
            return
246 921355f8 Giorgos Verigakis
        
247 921355f8 Giorgos Verigakis
        params = {
248 921355f8 Giorgos Verigakis
            'disk_format': disk_format,
249 921355f8 Giorgos Verigakis
            'is_public': self.public,
250 921355f8 Giorgos Verigakis
            'properties': {}}
251 921355f8 Giorgos Verigakis
        
252 921355f8 Giorgos Verigakis
        if self.meta:
253 921355f8 Giorgos Verigakis
            for m in self.meta:
254 921355f8 Giorgos Verigakis
                key, sep, val = m.partition('=')
255 921355f8 Giorgos Verigakis
                if key and val:
256 921355f8 Giorgos Verigakis
                    params['properties'][key] = val
257 921355f8 Giorgos Verigakis
                else:
258 921355f8 Giorgos Verigakis
                    print 'WARNING: Ignoring meta', m
259 921355f8 Giorgos Verigakis
        
260 921355f8 Giorgos Verigakis
        backend = ImageBackend(self.uid or settings.SYSTEM_IMAGES_OWNER)
261 921355f8 Giorgos Verigakis
        backend.register(name, url, params)
262 921355f8 Giorgos Verigakis
        backend.close()
263 8cdde414 Giorgos Verigakis
264 8cdde414 Giorgos Verigakis
265 5adfb622 Giorgos Verigakis
class UpdateImage(Command):
266 5adfb622 Giorgos Verigakis
    group = 'image'
267 5adfb622 Giorgos Verigakis
    name = 'update'
268 5adfb622 Giorgos Verigakis
    syntax = '<image id>'
269 5adfb622 Giorgos Verigakis
    description = 'update an image stored in Pithos'
270 5adfb622 Giorgos Verigakis
    
271 5adfb622 Giorgos Verigakis
    def add_options(self, parser):
272 5adfb622 Giorgos Verigakis
        container_formats = ', '.join(settings.ALLOWED_CONTAINER_FORMATS)
273 5adfb622 Giorgos Verigakis
        disk_formats = ', '.join(settings.ALLOWED_DISK_FORMATS)
274 5adfb622 Giorgos Verigakis
        
275 5adfb622 Giorgos Verigakis
        parser.add_option('--container-format', dest='container_format',
276 5adfb622 Giorgos Verigakis
                metavar='FORMAT',
277 5adfb622 Giorgos Verigakis
                help='set container format (%s)' % container_formats)
278 5adfb622 Giorgos Verigakis
        parser.add_option('--disk-format', dest='disk_format',
279 5adfb622 Giorgos Verigakis
                metavar='FORMAT',
280 5adfb622 Giorgos Verigakis
                help='set disk format (%s)' % disk_formats)
281 5adfb622 Giorgos Verigakis
        parser.add_option('--name', dest='name',
282 5adfb622 Giorgos Verigakis
                metavar='NAME',
283 5adfb622 Giorgos Verigakis
                help='set name to NAME')
284 5adfb622 Giorgos Verigakis
        parser.add_option('--private', action='store_true', dest='private',
285 5adfb622 Giorgos Verigakis
                help='make image private')
286 5adfb622 Giorgos Verigakis
        parser.add_option('--public', action='store_true', dest='public',
287 5adfb622 Giorgos Verigakis
                help='make image public')
288 5adfb622 Giorgos Verigakis
        parser.add_option('--user', dest='user',
289 921355f8 Giorgos Verigakis
                default=settings.SYSTEM_IMAGES_OWNER,
290 5adfb622 Giorgos Verigakis
                metavar='USER',
291 921355f8 Giorgos Verigakis
                help='connect as USER')
292 5adfb622 Giorgos Verigakis
    
293 5adfb622 Giorgos Verigakis
    def main(self, image_id):
294 5adfb622 Giorgos Verigakis
        backend = ImageBackend(self.user)
295 5adfb622 Giorgos Verigakis
        
296 6ef51e9f Giorgos Verigakis
        image = backend.get_image(image_id)
297 5adfb622 Giorgos Verigakis
        if not image:
298 5adfb622 Giorgos Verigakis
            print 'Image not found'
299 5adfb622 Giorgos Verigakis
            return
300 5adfb622 Giorgos Verigakis
        
301 5adfb622 Giorgos Verigakis
        params = {}
302 5adfb622 Giorgos Verigakis
        
303 5adfb622 Giorgos Verigakis
        if self.container_format:
304 5adfb622 Giorgos Verigakis
            if self.container_format not in settings.ALLOWED_CONTAINER_FORMATS:
305 5adfb622 Giorgos Verigakis
                print 'Invalid container format'
306 5adfb622 Giorgos Verigakis
                return
307 5adfb622 Giorgos Verigakis
            params['container_format'] = self.container_format
308 5adfb622 Giorgos Verigakis
        if self.disk_format:
309 5adfb622 Giorgos Verigakis
            if self.disk_format not in settings.ALLOWED_DISK_FORMATS:
310 5adfb622 Giorgos Verigakis
                print 'Invalid disk format'
311 5adfb622 Giorgos Verigakis
                return
312 5adfb622 Giorgos Verigakis
            params['disk_format'] = self.disk_format
313 5adfb622 Giorgos Verigakis
        if self.name:
314 5adfb622 Giorgos Verigakis
            params['name'] = self.name
315 5adfb622 Giorgos Verigakis
        if self.private:
316 5adfb622 Giorgos Verigakis
            params['is_public'] = False
317 5adfb622 Giorgos Verigakis
        if self.public:
318 5adfb622 Giorgos Verigakis
            params['is_public'] = True
319 5adfb622 Giorgos Verigakis
        
320 5adfb622 Giorgos Verigakis
        backend.update(image_id, params)
321 5adfb622 Giorgos Verigakis
        backend.close()
322 5adfb622 Giorgos Verigakis
323 5adfb622 Giorgos Verigakis
324 8cdde414 Giorgos Verigakis
class ModifyImage(Command):
325 9a60dacb Giorgos Verigakis
    group = 'image'
326 8cdde414 Giorgos Verigakis
    name = 'modify'
327 8cdde414 Giorgos Verigakis
    syntax = '<image id>'
328 8cdde414 Giorgos Verigakis
    description = 'modify an image'
329 8cdde414 Giorgos Verigakis
    
330 8cdde414 Giorgos Verigakis
    def add_options(self, parser):
331 d1ceb341 Giorgos Verigakis
        states = ', '.join(x[0] for x in models.Image.IMAGE_STATES)
332 d1ceb341 Giorgos Verigakis
        formats = ', '.join(x[0] for x in models.Image.FORMATS)
333 d1ceb341 Giorgos Verigakis
334 8cdde414 Giorgos Verigakis
        parser.add_option('-b', dest='backend_id', metavar='BACKEND_ID',
335 5adfb622 Giorgos Verigakis
                help='set image backend id')
336 8cdde414 Giorgos Verigakis
        parser.add_option('-f', dest='format', metavar='FORMAT',
337 5adfb622 Giorgos Verigakis
                help='set image format (%s)' % formats)
338 8cdde414 Giorgos Verigakis
        parser.add_option('-n', dest='name', metavar='NAME',
339 5adfb622 Giorgos Verigakis
                help='set image name')
340 ca8f8081 Giorgos Verigakis
        parser.add_option('--public', action='store_true', dest='public',
341 5adfb622 Giorgos Verigakis
                help='make image public')
342 ca8f8081 Giorgos Verigakis
        parser.add_option('--nopublic', action='store_true', dest='private',
343 5adfb622 Giorgos Verigakis
                help='make image private')
344 5adfb622 Giorgos Verigakis
        parser.add_option('-s', dest='state', metavar='STATE',
345 5adfb622 Giorgos Verigakis
                help='set image state (%s)' % states)
346 8cdde414 Giorgos Verigakis
        parser.add_option('-u', dest='uid', metavar='UID',
347 5adfb622 Giorgos Verigakis
                help='assign image to user with id UID')
348 8cdde414 Giorgos Verigakis
    
349 8cdde414 Giorgos Verigakis
    def main(self, image_id):
350 8cdde414 Giorgos Verigakis
        try:
351 8cdde414 Giorgos Verigakis
            image = models.Image.objects.get(id=image_id)
352 8cdde414 Giorgos Verigakis
        except:
353 8cdde414 Giorgos Verigakis
            print 'Image not found'
354 8cdde414 Giorgos Verigakis
            return
355 8cdde414 Giorgos Verigakis
        
356 8cdde414 Giorgos Verigakis
        if self.backend_id:
357 8cdde414 Giorgos Verigakis
            image.backend_id = self.backend_id
358 8cdde414 Giorgos Verigakis
        if self.format:
359 13d2b7f7 Giorgos Verigakis
            allowed = [x[0] for x in models.Image.FORMATS]
360 13d2b7f7 Giorgos Verigakis
            if self.format not in allowed:
361 d1ceb341 Giorgos Verigakis
                valid = ', '.join(allowed)
362 d1ceb341 Giorgos Verigakis
                print 'Invalid format. Must be one of:', valid
363 13d2b7f7 Giorgos Verigakis
                return
364 13d2b7f7 Giorgos Verigakis
            image.format = self.format
365 8cdde414 Giorgos Verigakis
        if self.name:
366 8cdde414 Giorgos Verigakis
            image.name = self.name
367 8cdde414 Giorgos Verigakis
        if self.public:
368 8cdde414 Giorgos Verigakis
            image.public = True
369 8cdde414 Giorgos Verigakis
        if self.private:
370 8cdde414 Giorgos Verigakis
            image.public = False
371 8cdde414 Giorgos Verigakis
        if self.state:
372 13d2b7f7 Giorgos Verigakis
            allowed = [x[0] for x in models.Image.IMAGE_STATES]
373 13d2b7f7 Giorgos Verigakis
            if self.state not in allowed:
374 d1ceb341 Giorgos Verigakis
                valid = ', '.join(allowed)
375 d1ceb341 Giorgos Verigakis
                print 'Invalid state. Must be one of:', valid
376 13d2b7f7 Giorgos Verigakis
                return
377 8cdde414 Giorgos Verigakis
            image.state = self.state
378 244c552b Giorgos Verigakis
        
379 244c552b Giorgos Verigakis
        image.userid = self.uid
380 8cdde414 Giorgos Verigakis
        
381 8cdde414 Giorgos Verigakis
        image.save()
382 13d2b7f7 Giorgos Verigakis
        print_item(image)
383 8cdde414 Giorgos Verigakis
384 8cdde414 Giorgos Verigakis
385 ca8f8081 Giorgos Verigakis
class ModifyImageMeta(Command):
386 9a60dacb Giorgos Verigakis
    group = 'image'
387 ca8f8081 Giorgos Verigakis
    name = 'meta'
388 ca8f8081 Giorgos Verigakis
    syntax = '<image id> [key[=val]]'
389 ca8f8081 Giorgos Verigakis
    description = 'get and manipulate image metadata'
390 ca8f8081 Giorgos Verigakis
    
391 921355f8 Giorgos Verigakis
    def add_options(self, parser):
392 921355f8 Giorgos Verigakis
        parser.add_option('--user', dest='user',
393 921355f8 Giorgos Verigakis
                default=settings.SYSTEM_IMAGES_OWNER,
394 921355f8 Giorgos Verigakis
                metavar='USER',
395 921355f8 Giorgos Verigakis
                help='connect as USER')
396 921355f8 Giorgos Verigakis
397 ca8f8081 Giorgos Verigakis
    def main(self, image_id, arg=''):
398 921355f8 Giorgos Verigakis
        if not image_id.isdigit():
399 921355f8 Giorgos Verigakis
            return self.main_pithos(image_id, arg)
400 921355f8 Giorgos Verigakis
        
401 ca8f8081 Giorgos Verigakis
        try:
402 ca8f8081 Giorgos Verigakis
            image = models.Image.objects.get(id=image_id)
403 ca8f8081 Giorgos Verigakis
        except:
404 ca8f8081 Giorgos Verigakis
            print 'Image not found'
405 ca8f8081 Giorgos Verigakis
            return
406 ca8f8081 Giorgos Verigakis
        
407 ca8f8081 Giorgos Verigakis
        key, sep, val = arg.partition('=')
408 ca8f8081 Giorgos Verigakis
        if not sep:
409 ca8f8081 Giorgos Verigakis
            val = None
410 ca8f8081 Giorgos Verigakis
        
411 ca8f8081 Giorgos Verigakis
        if not key:
412 ca8f8081 Giorgos Verigakis
            metadata = {}
413 7cc3c7d9 Giorgos Verigakis
            for meta in image.metadata.order_by('meta_key'):
414 ca8f8081 Giorgos Verigakis
                metadata[meta.meta_key] = meta.meta_value
415 ca8f8081 Giorgos Verigakis
            print_dict(metadata)
416 ca8f8081 Giorgos Verigakis
            return
417 ca8f8081 Giorgos Verigakis
        
418 ca8f8081 Giorgos Verigakis
        try:
419 7cc3c7d9 Giorgos Verigakis
            meta = image.metadata.get(meta_key=key)
420 ca8f8081 Giorgos Verigakis
        except models.ImageMetadata.DoesNotExist:
421 ca8f8081 Giorgos Verigakis
            meta = None
422 ca8f8081 Giorgos Verigakis
        
423 ca8f8081 Giorgos Verigakis
        if val is None:
424 ca8f8081 Giorgos Verigakis
            if meta:
425 ca8f8081 Giorgos Verigakis
                print_dict({key: meta.meta_value})
426 ca8f8081 Giorgos Verigakis
            return
427 ca8f8081 Giorgos Verigakis
        
428 ca8f8081 Giorgos Verigakis
        if val:
429 ca8f8081 Giorgos Verigakis
            if not meta:
430 7cc3c7d9 Giorgos Verigakis
                meta = image.metadata.create(meta_key=key)
431 ca8f8081 Giorgos Verigakis
            meta.meta_value = val
432 ca8f8081 Giorgos Verigakis
            meta.save()
433 ca8f8081 Giorgos Verigakis
        else:
434 ca8f8081 Giorgos Verigakis
            # Delete if val is empty
435 ca8f8081 Giorgos Verigakis
            if meta:
436 ca8f8081 Giorgos Verigakis
                meta.delete()
437 921355f8 Giorgos Verigakis
    
438 921355f8 Giorgos Verigakis
    def main_pithos(self, image_id, arg=''):
439 921355f8 Giorgos Verigakis
        backend = ImageBackend(self.user)
440 921355f8 Giorgos Verigakis
                
441 921355f8 Giorgos Verigakis
        try:
442 6ef51e9f Giorgos Verigakis
            image = backend.get_image(image_id)
443 921355f8 Giorgos Verigakis
            if not image:
444 921355f8 Giorgos Verigakis
                print 'Image not found'
445 921355f8 Giorgos Verigakis
                return
446 921355f8 Giorgos Verigakis
            
447 921355f8 Giorgos Verigakis
            key, sep, val = arg.partition('=')
448 921355f8 Giorgos Verigakis
            if not sep:
449 921355f8 Giorgos Verigakis
                val = None
450 921355f8 Giorgos Verigakis
            
451 921355f8 Giorgos Verigakis
            properties = image.get('properties', {})
452 921355f8 Giorgos Verigakis
            
453 921355f8 Giorgos Verigakis
            if not key:
454 921355f8 Giorgos Verigakis
                print_dict(properties)
455 921355f8 Giorgos Verigakis
                return
456 921355f8 Giorgos Verigakis
            
457 921355f8 Giorgos Verigakis
            if val is None:
458 921355f8 Giorgos Verigakis
                if key in properties:
459 921355f8 Giorgos Verigakis
                    print_dict({key: properties[key]})
460 921355f8 Giorgos Verigakis
                return
461 921355f8 Giorgos Verigakis
            
462 921355f8 Giorgos Verigakis
            if val:
463 921355f8 Giorgos Verigakis
                properties[key] = val        
464 921355f8 Giorgos Verigakis
                params = {'properties': properties}
465 921355f8 Giorgos Verigakis
                backend.update(image_id, params)
466 921355f8 Giorgos Verigakis
        finally:
467 921355f8 Giorgos Verigakis
            backend.close()
468 ca8f8081 Giorgos Verigakis
469 ca8f8081 Giorgos Verigakis
470 910f53d9 Giorgos Verigakis
# Flavor commands
471 910f53d9 Giorgos Verigakis
472 910f53d9 Giorgos Verigakis
class CreateFlavor(Command):
473 910f53d9 Giorgos Verigakis
    group = 'flavor'
474 910f53d9 Giorgos Verigakis
    name = 'create'
475 910f53d9 Giorgos Verigakis
    syntax = '<cpu>[,<cpu>,...] <ram>[,<ram>,...] <disk>[,<disk>,...]'
476 910f53d9 Giorgos Verigakis
    description = 'create one or more flavors'
477 910f53d9 Giorgos Verigakis
    
478 c909cbbd Giorgos Verigakis
    def add_options(self, parser):
479 c909cbbd Giorgos Verigakis
        disk_templates = ', '.join(t for t in settings.GANETI_DISK_TEMPLATES)
480 c909cbbd Giorgos Verigakis
        parser.add_option('--disk-template',
481 c909cbbd Giorgos Verigakis
            dest='disk_template',
482 c909cbbd Giorgos Verigakis
            metavar='TEMPLATE',
483 c909cbbd Giorgos Verigakis
            default=settings.DEFAULT_GANETI_DISK_TEMPLATE,
484 c909cbbd Giorgos Verigakis
            help='available disk templates: %s' % disk_templates)
485 c909cbbd Giorgos Verigakis
    
486 910f53d9 Giorgos Verigakis
    def main(self, cpu, ram, disk):
487 910f53d9 Giorgos Verigakis
        cpus = cpu.split(',')
488 910f53d9 Giorgos Verigakis
        rams = ram.split(',')
489 910f53d9 Giorgos Verigakis
        disks = disk.split(',')
490 910f53d9 Giorgos Verigakis
        
491 910f53d9 Giorgos Verigakis
        flavors = []
492 910f53d9 Giorgos Verigakis
        for cpu, ram, disk in product(cpus, rams, disks):
493 910f53d9 Giorgos Verigakis
            try:
494 910f53d9 Giorgos Verigakis
                flavors.append((int(cpu), int(ram), int(disk)))
495 910f53d9 Giorgos Verigakis
            except ValueError:
496 910f53d9 Giorgos Verigakis
                print 'Invalid values'
497 910f53d9 Giorgos Verigakis
                return
498 910f53d9 Giorgos Verigakis
        
499 910f53d9 Giorgos Verigakis
        created = []
500 c909cbbd Giorgos Verigakis
        
501 910f53d9 Giorgos Verigakis
        for cpu, ram, disk in flavors:
502 c909cbbd Giorgos Verigakis
            flavor = models.Flavor.objects.create(
503 c909cbbd Giorgos Verigakis
                cpu=cpu,
504 c909cbbd Giorgos Verigakis
                ram=ram,
505 c909cbbd Giorgos Verigakis
                disk=disk,
506 c909cbbd Giorgos Verigakis
                disk_template=self.disk_template)
507 910f53d9 Giorgos Verigakis
            created.append(flavor)
508 910f53d9 Giorgos Verigakis
        
509 910f53d9 Giorgos Verigakis
        print_items(created, detail=True)
510 910f53d9 Giorgos Verigakis
511 910f53d9 Giorgos Verigakis
512 910f53d9 Giorgos Verigakis
class DeleteFlavor(Command):
513 910f53d9 Giorgos Verigakis
    group = 'flavor'
514 910f53d9 Giorgos Verigakis
    name = 'delete'
515 910f53d9 Giorgos Verigakis
    syntax = '<flavor id> [<flavor id>] [...]'
516 910f53d9 Giorgos Verigakis
    description = 'delete one or more flavors'
517 910f53d9 Giorgos Verigakis
    
518 910f53d9 Giorgos Verigakis
    def main(self, *args):
519 910f53d9 Giorgos Verigakis
        if not args:
520 910f53d9 Giorgos Verigakis
            raise TypeError
521 910f53d9 Giorgos Verigakis
        for flavor_id in args:
522 910f53d9 Giorgos Verigakis
            flavor = models.Flavor.objects.get(id=int(flavor_id))
523 870c24a0 Giorgos Verigakis
            flavor.deleted = True
524 870c24a0 Giorgos Verigakis
            flavor.save()
525 910f53d9 Giorgos Verigakis
526 910f53d9 Giorgos Verigakis
527 910f53d9 Giorgos Verigakis
class ListFlavors(Command):
528 910f53d9 Giorgos Verigakis
    group = 'flavor'
529 910f53d9 Giorgos Verigakis
    name = 'list'
530 910f53d9 Giorgos Verigakis
    syntax = '[flavor id]'
531 910f53d9 Giorgos Verigakis
    description = 'list images'
532 910f53d9 Giorgos Verigakis
    
533 910f53d9 Giorgos Verigakis
    def add_options(self, parser):
534 79c33f4e Giorgos Verigakis
        parser.add_option('-a', action='store_true', dest='show_deleted',
535 79c33f4e Giorgos Verigakis
                default=False, help='also list deleted flavors')
536 910f53d9 Giorgos Verigakis
        parser.add_option('-l', action='store_true', dest='detail',
537 910f53d9 Giorgos Verigakis
                        default=False, help='show detailed output')
538 910f53d9 Giorgos Verigakis
    
539 910f53d9 Giorgos Verigakis
    def main(self, flavor_id=None):
540 910f53d9 Giorgos Verigakis
        if flavor_id:
541 910f53d9 Giorgos Verigakis
            flavors = [models.Flavor.objects.get(id=flavor_id)]
542 910f53d9 Giorgos Verigakis
        else:
543 79c33f4e Giorgos Verigakis
            flavors = models.Flavor.objects.order_by('id')
544 79c33f4e Giorgos Verigakis
            if not self.show_deleted:
545 79c33f4e Giorgos Verigakis
                flavors = flavors.exclude(deleted=True)
546 910f53d9 Giorgos Verigakis
        print_items(flavors, self.detail)
547 910f53d9 Giorgos Verigakis
548 910f53d9 Giorgos Verigakis
549 87c1dadc Giorgos Verigakis
class ShowStats(Command):
550 87c1dadc Giorgos Verigakis
    group = 'stats'
551 87c1dadc Giorgos Verigakis
    name = None
552 87c1dadc Giorgos Verigakis
    description = 'show statistics'
553 87c1dadc Giorgos Verigakis
554 87c1dadc Giorgos Verigakis
    def main(self):
555 87c1dadc Giorgos Verigakis
        stats = {}
556 87c1dadc Giorgos Verigakis
        stats['Images'] = models.Image.objects.exclude(state='DELETED').count()
557 87c1dadc Giorgos Verigakis
        stats['Flavors'] = models.Flavor.objects.count()
558 87c1dadc Giorgos Verigakis
        stats['VMs'] = models.VirtualMachine.objects.filter(deleted=False).count()
559 87c1dadc Giorgos Verigakis
        stats['Networks'] = models.Network.objects.exclude(state='DELETED').count()
560 87c1dadc Giorgos Verigakis
        
561 87c1dadc Giorgos Verigakis
        stats['Ganeti Instances'] = len(backend.get_ganeti_instances())
562 87c1dadc Giorgos Verigakis
        stats['Ganeti Nodes'] = len(backend.get_ganeti_nodes())
563 87c1dadc Giorgos Verigakis
        stats['Ganeti Jobs'] = len(backend.get_ganeti_jobs())
564 87c1dadc Giorgos Verigakis
        
565 87c1dadc Giorgos Verigakis
        print_dict(stats)
566 87c1dadc Giorgos Verigakis
567 87c1dadc Giorgos Verigakis
568 9a60dacb Giorgos Verigakis
def print_usage(exe, groups, group=None, shortcut=False):
569 9a60dacb Giorgos Verigakis
    nop = Command(exe, [])
570 9a60dacb Giorgos Verigakis
    nop.parser.print_help()
571 9a60dacb Giorgos Verigakis
    if group:
572 9a60dacb Giorgos Verigakis
        groups = {group: groups[group]}
573 8cdde414 Giorgos Verigakis
574 8cdde414 Giorgos Verigakis
    print
575 8cdde414 Giorgos Verigakis
    print 'Commands:'
576 9a60dacb Giorgos Verigakis
    
577 9a60dacb Giorgos Verigakis
    for group, commands in sorted(groups.items()):
578 9a60dacb Giorgos Verigakis
        for command, cls in sorted(commands.items()):
579 735f44fe Giorgos Verigakis
            if cls.hidden:
580 735f44fe Giorgos Verigakis
                continue
581 ead18dbc Giorgos Verigakis
            name = '  %s %s' % (group, command or '')
582 9a60dacb Giorgos Verigakis
            print '%s %s' % (name.ljust(22), cls.description)
583 9a60dacb Giorgos Verigakis
        print
584 8cdde414 Giorgos Verigakis
585 8cdde414 Giorgos Verigakis
586 8cdde414 Giorgos Verigakis
def main():
587 d19e8f77 Giorgos Verigakis
    dictConfig(settings.SNFADMIN_LOGGING)
588 9a60dacb Giorgos Verigakis
    groups = defaultdict(dict)
589 8cdde414 Giorgos Verigakis
    module = sys.modules[__name__]
590 8cdde414 Giorgos Verigakis
    for name, cls in inspect.getmembers(module, inspect.isclass):
591 9a60dacb Giorgos Verigakis
        if not issubclass(cls, Command) or cls == Command:
592 9a60dacb Giorgos Verigakis
            continue
593 9a60dacb Giorgos Verigakis
        groups[cls.group][cls.name] = cls
594 8cdde414 Giorgos Verigakis
    
595 8cdde414 Giorgos Verigakis
    argv = list(sys.argv)
596 8cdde414 Giorgos Verigakis
    exe = basename(argv.pop(0))
597 9a60dacb Giorgos Verigakis
    prefix, sep, suffix = exe.partition('-')
598 9a60dacb Giorgos Verigakis
    if sep and prefix == 'snf' and suffix in groups:
599 9a60dacb Giorgos Verigakis
        # Allow shortcut aliases like snf-image, snf-server, etc
600 9a60dacb Giorgos Verigakis
        group = suffix
601 8cdde414 Giorgos Verigakis
    else:
602 9a60dacb Giorgos Verigakis
        group = argv.pop(0) if argv else None
603 9a60dacb Giorgos Verigakis
        if group in groups:
604 9a60dacb Giorgos Verigakis
            exe = '%s %s' % (exe, group)
605 9a60dacb Giorgos Verigakis
        else:
606 9a60dacb Giorgos Verigakis
            exe = '%s <group>' % exe
607 9a60dacb Giorgos Verigakis
            group = None
608 8cdde414 Giorgos Verigakis
    
609 8cdde414 Giorgos Verigakis
    command = argv.pop(0) if argv else None
610 8cdde414 Giorgos Verigakis
    
611 9a60dacb Giorgos Verigakis
    if group not in groups or command not in groups[group]:
612 9a60dacb Giorgos Verigakis
        print_usage(exe, groups, group)
613 8cdde414 Giorgos Verigakis
        sys.exit(1)
614 8cdde414 Giorgos Verigakis
    
615 9a60dacb Giorgos Verigakis
    cls = groups[group][command]
616 9a60dacb Giorgos Verigakis
    cmd = cls(exe, argv)
617 8cdde414 Giorgos Verigakis
    cmd.execute()
618 8cdde414 Giorgos Verigakis
619 8cdde414 Giorgos Verigakis
620 8cdde414 Giorgos Verigakis
if __name__ == '__main__':
621 8cdde414 Giorgos Verigakis
    main()