|
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 |
margin = max(len(key) for key in d) + 1
|
|
67 |
|
|
68 |
for key, val in sorted(d.items()):
|
|
69 |
if key in exclude:
|
|
70 |
continue
|
|
71 |
print '%s: %s' % (key.rjust(margin), val)
|
|
72 |
|
|
73 |
def print_items(items, detail=False):
|
|
74 |
for item in items:
|
|
75 |
print '%d %s' % (item.id, item.name)
|
|
76 |
if detail:
|
|
77 |
print_dict(item.__dict__, exclude=('id', 'name', '_state'))
|
|
78 |
print
|
|
79 |
|
|
80 |
|
|
81 |
class Command(object):
|
|
82 |
category = ''
|
|
83 |
name = ''
|
|
84 |
syntax = ''
|
|
85 |
description = ''
|
|
86 |
|
|
87 |
def __init__(self, argv):
|
|
88 |
parser = OptionParser()
|
|
89 |
parser.usage = '%%prog %s %s %s' % (self.category, self.name,
|
|
90 |
self.syntax)
|
|
91 |
self.add_options(parser)
|
|
92 |
options, self.args = parser.parse_args(argv)
|
|
93 |
|
|
94 |
# Add options to self
|
|
95 |
for opt in parser.option_list:
|
|
96 |
key = opt.dest
|
|
97 |
if key:
|
|
98 |
val = getattr(options, key)
|
|
99 |
setattr(self, key, val)
|
|
100 |
|
|
101 |
ch = logging.StreamHandler()
|
|
102 |
ch.setFormatter(logging.Formatter('%(message)s'))
|
|
103 |
logger = logging.getLogger()
|
|
104 |
logger.addHandler(ch)
|
|
105 |
level = logging.WARNING
|
|
106 |
logger.setLevel(level)
|
|
107 |
|
|
108 |
self.parser = parser
|
|
109 |
|
|
110 |
def add_options(self, parser):
|
|
111 |
pass
|
|
112 |
|
|
113 |
def execute(self):
|
|
114 |
try:
|
|
115 |
self.main(*self.args)
|
|
116 |
except TypeError:
|
|
117 |
self.parser.print_help()
|
|
118 |
|
|
119 |
|
|
120 |
class ListServers(Command):
|
|
121 |
category = 'server'
|
|
122 |
name = 'ls'
|
|
123 |
syntax = '[server id]'
|
|
124 |
description = 'list servers'
|
|
125 |
|
|
126 |
def add_options(self, parser):
|
|
127 |
parser.add_option('-a', action='store_true', dest='show_deleted',
|
|
128 |
default=False, help='also list deleted servers')
|
|
129 |
parser.add_option('-l', action='store_true', dest='detail',
|
|
130 |
default=False, help='show detailed output')
|
|
131 |
parser.add_option('-u', dest='uid', metavar='UID',
|
|
132 |
help='show servers of user with id UID')
|
|
133 |
|
|
134 |
def main(self, server_id=None):
|
|
135 |
servers = models.VirtualMachine.objects.order_by('id')
|
|
136 |
if server_id:
|
|
137 |
servers = servers.filter(id=server_id)
|
|
138 |
if not self.show_deleted:
|
|
139 |
servers = servers.filter(deleted=False)
|
|
140 |
if self.uid:
|
|
141 |
user = get_user(self.uid)
|
|
142 |
if user:
|
|
143 |
servers = servers.filter(owner=user)
|
|
144 |
else:
|
|
145 |
print 'Unknown user id'
|
|
146 |
return
|
|
147 |
|
|
148 |
print_items(servers, self.detail)
|
|
149 |
|
|
150 |
|
|
151 |
class ListUsers(Command):
|
|
152 |
category = 'user'
|
|
153 |
name = 'ls'
|
|
154 |
syntax = '[user id]'
|
|
155 |
description = 'list users'
|
|
156 |
|
|
157 |
def add_options(self, parser):
|
|
158 |
parser.add_option('-l', action='store_true', dest='detail',
|
|
159 |
default=False, help='show detailed output')
|
|
160 |
|
|
161 |
def main(self, user_id=None):
|
|
162 |
if user_id:
|
|
163 |
users = [models.SynnefoUser.objects.get(id=user_id)]
|
|
164 |
else:
|
|
165 |
users = models.SynnefoUser.objects.order_by('id')
|
|
166 |
print_items(users, self.detail)
|
|
167 |
|
|
168 |
|
|
169 |
class ListImages(Command):
|
|
170 |
category = 'image'
|
|
171 |
name = 'ls'
|
|
172 |
syntax = '[image id]'
|
|
173 |
description = 'list images'
|
|
174 |
|
|
175 |
def add_options(self, parser):
|
|
176 |
parser.add_option('-l', action='store_true', dest='detail',
|
|
177 |
default=False, help='show detailed output')
|
|
178 |
|
|
179 |
def main(self, image_id=None):
|
|
180 |
if image_id:
|
|
181 |
images = [models.Image.objects.get(id=image_id)]
|
|
182 |
else:
|
|
183 |
images = models.Image.objects.order_by('id')
|
|
184 |
print_items(images, self.detail)
|
|
185 |
|
|
186 |
|
|
187 |
class RegisterImage(Command):
|
|
188 |
category = 'image'
|
|
189 |
name = 'register'
|
|
190 |
syntax = '<name> <backend id>'
|
|
191 |
description = 'register an image'
|
|
192 |
|
|
193 |
def add_options(self, parser):
|
|
194 |
parser.add_option('-p', action='store_true', dest='public',
|
|
195 |
default=False, help='make image public')
|
|
196 |
parser.add_option('-u', dest='uid', metavar='UID',
|
|
197 |
help='assign image to user with id UID')
|
|
198 |
|
|
199 |
def main(self, name, backend_id):
|
|
200 |
user = None
|
|
201 |
if self.uid:
|
|
202 |
user = get_user(self.uid)
|
|
203 |
if not user:
|
|
204 |
print 'Unknown user id'
|
|
205 |
return
|
|
206 |
|
|
207 |
image = models.Image.objects.create(
|
|
208 |
name=name,
|
|
209 |
state='ACTIVE',
|
|
210 |
owner=user,
|
|
211 |
backend_id=backend_id,
|
|
212 |
public=self.public)
|
|
213 |
|
|
214 |
|
|
215 |
class ModifyImage(Command):
|
|
216 |
category = 'image'
|
|
217 |
name = 'modify'
|
|
218 |
syntax = '<image id>'
|
|
219 |
description = 'modify an image'
|
|
220 |
|
|
221 |
def add_options(self, parser):
|
|
222 |
parser.add_option('-b', dest='backend_id', metavar='BACKEND_ID',
|
|
223 |
help='set image backend id')
|
|
224 |
parser.add_option('-f', dest='format', metavar='FORMAT',
|
|
225 |
help='set image format')
|
|
226 |
parser.add_option('-n', dest='name', metavar='NAME',
|
|
227 |
help='set image name')
|
|
228 |
parser.add_option('-p', action='store_true', dest='public',
|
|
229 |
default=False, help='make image public')
|
|
230 |
parser.add_option('-r', action='store_true', dest='private',
|
|
231 |
default=False, help='make image private')
|
|
232 |
parser.add_option('-s', dest='state', metavar='STATE',
|
|
233 |
default=False, help='set image state')
|
|
234 |
parser.add_option('-u', dest='uid', metavar='UID',
|
|
235 |
help='assign image to user with id UID')
|
|
236 |
|
|
237 |
def main(self, image_id):
|
|
238 |
try:
|
|
239 |
image = models.Image.objects.get(id=image_id)
|
|
240 |
except:
|
|
241 |
print 'Image not found'
|
|
242 |
return
|
|
243 |
|
|
244 |
if self.backend_id:
|
|
245 |
image.backend_id = self.backend_id
|
|
246 |
if self.format:
|
|
247 |
image.format = format
|
|
248 |
if self.name:
|
|
249 |
image.name = self.name
|
|
250 |
if self.public:
|
|
251 |
image.public = True
|
|
252 |
if self.private:
|
|
253 |
image.public = False
|
|
254 |
if self.state:
|
|
255 |
image.state = self.state
|
|
256 |
if self.uid:
|
|
257 |
image.owner = get_user(self.uid)
|
|
258 |
|
|
259 |
image.save()
|
|
260 |
|
|
261 |
|
|
262 |
def print_categories(exe, categories):
|
|
263 |
print 'Usage: %s <category> <command>' % exe
|
|
264 |
print
|
|
265 |
print 'Categories:'
|
|
266 |
for category in sorted(categories):
|
|
267 |
print ' %s' % category
|
|
268 |
|
|
269 |
def print_commands(exe, commands):
|
|
270 |
print 'Usage: %s <command>' % exe
|
|
271 |
print
|
|
272 |
print 'Commands:'
|
|
273 |
for command, cls in sorted(commands.items()):
|
|
274 |
print ' %s %s' % (command.ljust(10), cls.description)
|
|
275 |
|
|
276 |
|
|
277 |
def main():
|
|
278 |
categories = defaultdict(dict)
|
|
279 |
module = sys.modules[__name__]
|
|
280 |
for name, cls in inspect.getmembers(module, inspect.isclass):
|
|
281 |
if issubclass(cls, Command) and cls != Command:
|
|
282 |
categories[cls.category][cls.name] = cls
|
|
283 |
|
|
284 |
argv = list(sys.argv)
|
|
285 |
exe = basename(argv.pop(0))
|
|
286 |
snf, sep, rest = exe.partition('-')
|
|
287 |
if snf == 'snf' and sep and rest != 'admin':
|
|
288 |
category = rest
|
|
289 |
if category not in categories:
|
|
290 |
print 'Invalid alias'
|
|
291 |
sys.exit(1)
|
|
292 |
else:
|
|
293 |
category = argv.pop(0) if argv else None
|
|
294 |
if category:
|
|
295 |
exe = '%s %s' % (exe, category)
|
|
296 |
|
|
297 |
if not category or category not in categories:
|
|
298 |
print_categories(exe, categories)
|
|
299 |
sys.exit(1)
|
|
300 |
|
|
301 |
command = argv.pop(0) if argv else None
|
|
302 |
|
|
303 |
if not command or command not in categories[category]:
|
|
304 |
print_commands(exe, categories[category])
|
|
305 |
sys.exit(1)
|
|
306 |
|
|
307 |
cls = categories[category][command]
|
|
308 |
cmd = cls(argv)
|
|
309 |
cmd.execute()
|
|
310 |
|
|
311 |
|
|
312 |
if __name__ == '__main__':
|
|
313 |
main()
|