~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to bin/nova-manage

  • Committer: NTT PF Lab.
  • Date: 2010-12-24 11:38:49 UTC
  • mto: This revision was merged to the branch mainline in revision 564.
  • Revision ID: openstack@lab.ntt.co.jp-20101224113849-z9nemzmki17bxnvw
SupportĀ IPv6

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
  CLI interface for nova management.
54
54
"""
55
55
 
56
 
import ast
57
56
import gettext
58
 
import glob
59
 
import json
60
 
import math
61
 
import netaddr
62
 
from optparse import OptionParser
 
57
import logging
63
58
import os
64
59
import sys
65
60
import time
66
61
 
 
62
import IPy
67
63
 
68
64
# If ../nova/__init__.py exists, add ../ to Python search path, so that
69
65
# it will override what happens to be installed in /usr/(local/)lib/python...
70
 
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
 
66
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
71
67
                                   os.pardir,
72
68
                                   os.pardir))
73
 
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
74
 
    sys.path.insert(0, POSSIBLE_TOPDIR)
 
69
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
 
70
    sys.path.insert(0, possible_topdir)
75
71
 
76
72
gettext.install('nova', unicode=1)
77
73
 
78
74
from nova import context
79
 
from nova import crypto
80
75
from nova import db
81
76
from nova import exception
82
77
from nova import flags
83
 
from nova import image
84
 
from nova import log as logging
85
78
from nova import quota
86
 
from nova import rpc
87
79
from nova import utils
88
 
from nova import version
89
 
from nova import vsa
90
 
from nova.api.ec2 import ec2utils
91
80
from nova.auth import manager
92
81
from nova.cloudpipe import pipelib
93
 
from nova.compute import instance_types
94
 
from nova.db import migration
95
 
from nova.volume import volume_types
 
82
 
96
83
 
97
84
FLAGS = flags.FLAGS
98
85
flags.DECLARE('fixed_range', 'nova.network.manager')
101
88
flags.DECLARE('vlan_start', 'nova.network.manager')
102
89
flags.DECLARE('vpn_start', 'nova.network.manager')
103
90
flags.DECLARE('fixed_range_v6', 'nova.network.manager')
104
 
flags.DECLARE('gateway_v6', 'nova.network.manager')
105
 
flags.DECLARE('libvirt_type', 'nova.virt.libvirt.connection')
106
 
flags.DEFINE_flag(flags.HelpFlag())
107
 
flags.DEFINE_flag(flags.HelpshortFlag())
108
 
flags.DEFINE_flag(flags.HelpXMLFlag())
109
 
 
110
 
 
111
 
# Decorators for actions
112
 
def args(*args, **kwargs):
113
 
    def _decorator(func):
114
 
        func.__dict__.setdefault('options', []).insert(0, (args, kwargs))
115
 
        return func
116
 
    return _decorator
117
 
 
118
 
 
119
 
def param2id(object_id):
120
 
    """Helper function to convert various id types to internal id.
121
 
    args: [object_id], e.g. 'vol-0000000a' or 'volume-0000000a' or '10'
122
 
    """
123
 
    if '-' in object_id:
124
 
        return ec2utils.ec2_id_to_id(object_id)
125
 
    else:
126
 
        return int(object_id)
127
 
 
128
91
 
129
92
class VpnCommands(object):
130
93
    """Class for managing VPNs."""
133
96
        self.manager = manager.AuthManager()
134
97
        self.pipe = pipelib.CloudPipe()
135
98
 
136
 
    @args('--project', dest="project", metavar='<Project name>',
137
 
            help='Project name')
138
 
    def list(self, project=None):
139
 
        """Print a listing of the VPN data for one or all projects."""
140
 
        print "WARNING: This method only works with deprecated auth"
 
99
    def list(self):
 
100
        """Print a listing of the VPNs for all projects."""
141
101
        print "%-12s\t" % 'project',
142
102
        print "%-20s\t" % 'ip:port',
143
 
        print "%-20s\t" % 'private_ip',
144
103
        print "%s" % 'state'
145
 
        if project:
146
 
            projects = [self.manager.get_project(project)]
147
 
        else:
148
 
            projects = self.manager.get_projects()
149
 
            # NOTE(vish): This hits the database a lot.  We could optimize
150
 
            #             by getting all networks in one query and all vpns
151
 
            #             in aother query, then doing lookups by project
152
 
        for project in projects:
 
104
        for project in self.manager.get_projects():
153
105
            print "%-12s\t" % project.name,
154
 
            ipport = "%s:%s" % (project.vpn_ip, project.vpn_port)
155
 
            print "%-20s\t" % ipport,
156
 
            ctxt = context.get_admin_context()
157
 
            vpn = db.instance_get_project_vpn(ctxt, project.id)
 
106
 
 
107
            try:
 
108
                s = "%s:%s" % (project.vpn_ip, project.vpn_port)
 
109
            except exception.NotFound:
 
110
                s = "None"
 
111
            print "%-20s\t" % s,
 
112
 
 
113
            vpn = self._vpn_for(project.id)
158
114
            if vpn:
159
 
                address = None
160
 
                state = 'down'
161
 
                if vpn.get('fixed_ip', None):
162
 
                    address = vpn['fixed_ip']['address']
163
 
                if project.vpn_ip and utils.vpn_ping(project.vpn_ip,
164
 
                                                     project.vpn_port):
165
 
                    state = 'up'
166
 
                print address,
167
 
                print vpn['host'],
168
 
                print ec2utils.id_to_ec2_id(vpn['id']),
169
 
                print vpn['vm_state'],
170
 
                print state
 
115
                command = "ping -c1 -w1 %s > /dev/null; echo $?"
 
116
                out, _err = utils.execute(command % vpn['private_dns_name'],
 
117
                                          check_exit_code=False)
 
118
                if out.strip() == '0':
 
119
                    net = 'up'
 
120
                else:
 
121
                    net = 'down'
 
122
                print vpn['private_dns_name'],
 
123
                print vpn['node_name'],
 
124
                print vpn['instance_id'],
 
125
                print vpn['state_description'],
 
126
                print net
 
127
 
171
128
            else:
172
129
                print None
173
130
 
 
131
    def _vpn_for(self, project_id):
 
132
        """Get the VPN instance for a project ID."""
 
133
        for instance in db.instance_get_all(context.get_admin_context()):
 
134
            if (instance['image_id'] == FLAGS.vpn_image_id
 
135
                and not instance['state_description'] in
 
136
                    ['shutting_down', 'shutdown']
 
137
                and instance['project_id'] == project_id):
 
138
                return instance
 
139
 
174
140
    def spawn(self):
175
141
        """Run all VPNs."""
176
 
        print "WARNING: This method only works with deprecated auth"
177
142
        for p in reversed(self.manager.get_projects()):
178
143
            if not self._vpn_for(p.id):
179
144
                print 'spawning %s' % p.id
180
 
                self.pipe.launch_vpn_instance(p.id, p.project_manager_id)
 
145
                self.pipe.launch_vpn_instance(p.id)
181
146
                time.sleep(10)
182
147
 
183
 
    @args('--project', dest="project_id", metavar='<Project name>',
184
 
            help='Project name')
185
 
    @args('--user', dest="user_id", metavar='<user name>', help='User name')
186
 
    def run(self, project_id, user_id):
187
 
        """Start the VPN for a given project and user."""
188
 
        if not user_id:
189
 
            print "WARNING: This method only works with deprecated auth"
190
 
            user_id = self.manager.get_project(project_id).project_manager_id
191
 
        self.pipe.launch_vpn_instance(project_id, user_id)
192
 
 
193
 
    @args('--project', dest="project_id", metavar='<Project name>',
194
 
            help='Project name')
195
 
    @args('--ip', dest="ip", metavar='<IP Address>', help='IP Address')
196
 
    @args('--port', dest="port", metavar='<Port>', help='Port')
197
 
    def change(self, project_id, ip, port):
198
 
        """Change the ip and port for a vpn.
199
 
 
200
 
        this will update all networks associated with a project
201
 
        not sure if that's the desired behavior or not, patches accepted
202
 
 
203
 
        """
204
 
        # TODO(tr3buchet): perhaps this shouldn't update all networks
205
 
        # associated with a project in the future
206
 
        admin_context = context.get_admin_context()
207
 
        networks = db.project_get_networks(admin_context, project_id)
208
 
        for network in networks:
209
 
            db.network_update(admin_context,
210
 
                              network['id'],
211
 
                              {'vpn_public_address': ip,
212
 
                               'vpn_public_port': int(port)})
 
148
    def run(self, project_id):
 
149
        """Start the VPN for a given project."""
 
150
        self.pipe.launch_vpn_instance(project_id)
213
151
 
214
152
 
215
153
class ShellCommands(object):
231
169
        Falls back to Python shell if unavailable"""
232
170
        self.run('python')
233
171
 
234
 
    @args('--shell', dest="shell", metavar='<bpython|ipython|python >',
235
 
            help='Python shell')
236
172
    def run(self, shell=None):
237
 
        """Runs a Python interactive interpreter."""
 
173
        """Runs a Python interactive interpreter.
 
174
 
 
175
        args: [shell=bpython]"""
238
176
        if not shell:
239
177
            shell = 'bpython'
240
178
 
268
206
                readline.parse_and_bind("tab:complete")
269
207
            code.interact()
270
208
 
271
 
    @args('--path', dest='path', metavar='<path>', help='Script path')
272
209
    def script(self, path):
273
210
        """Runs the script from the specifed path with flags set properly.
274
211
        arguments: path"""
281
218
    def __init__(self):
282
219
        self.manager = manager.AuthManager()
283
220
 
284
 
    @args('--user', dest="user", metavar='<user name>', help='User name')
285
 
    @args('--role', dest="role", metavar='<user role>', help='User role')
286
 
    @args('--project', dest="project", metavar='<Project name>',
287
 
            help='Project name')
288
221
    def add(self, user, role, project=None):
289
222
        """adds role to user
290
 
        if project is specified, adds project specific role"""
291
 
        if project:
292
 
            projobj = self.manager.get_project(project)
293
 
            if not projobj.has_member(user):
294
 
                print "%s not a member of %s" % (user, project)
295
 
                return
 
223
        if project is specified, adds project specific role
 
224
        arguments: user, role [project]"""
296
225
        self.manager.add_role(user, role, project)
297
226
 
298
 
    @args('--user', dest="user", metavar='<user name>', help='User name')
299
 
    @args('--role', dest="role", metavar='<user role>', help='User role')
300
 
    @args('--project', dest="project", metavar='<Project name>',
301
 
            help='Project name')
302
227
    def has(self, user, role, project=None):
303
228
        """checks to see if user has role
304
229
        if project is specified, returns True if user has
305
 
        the global role and the project role"""
 
230
        the global role and the project role
 
231
        arguments: user, role [project]"""
306
232
        print self.manager.has_role(user, role, project)
307
233
 
308
 
    @args('--user', dest="user", metavar='<user name>', help='User name')
309
 
    @args('--role', dest="role", metavar='<user role>', help='User role')
310
 
    @args('--project', dest="project", metavar='<Project name>',
311
 
            help='Project name')
312
234
    def remove(self, user, role, project=None):
313
235
        """removes role from user
314
 
        if project is specified, removes project specific role"""
 
236
        if project is specified, removes project specific role
 
237
        arguments: user, role [project]"""
315
238
        self.manager.remove_role(user, role, project)
316
239
 
317
240
 
318
 
def _db_error(caught_exception):
319
 
    print caught_exception
320
 
    print _("The above error may show that the database has not "
321
 
            "been created.\nPlease create a database using "
322
 
            "'nova-manage db sync' before running this command.")
323
 
    exit(1)
324
 
 
325
 
 
326
241
class UserCommands(object):
327
242
    """Class for managing users."""
328
243
 
335
250
    def __init__(self):
336
251
        self.manager = manager.AuthManager()
337
252
 
338
 
    @args('--name', dest="name", metavar='<admin name>', help='Admin name')
339
 
    @args('--access', dest="access", metavar='<access>', help='Access')
340
 
    @args('--secret', dest="secret", metavar='<secret>', help='Secret')
341
253
    def admin(self, name, access=None, secret=None):
342
 
        """creates a new admin and prints exports"""
343
 
        try:
344
 
            user = self.manager.create_user(name, access, secret, True)
345
 
        except exception.DBError, e:
346
 
            _db_error(e)
 
254
        """creates a new admin and prints exports
 
255
        arguments: name [access] [secret]"""
 
256
        user = self.manager.create_user(name, access, secret, True)
347
257
        self._print_export(user)
348
258
 
349
 
    @args('--name', dest="name", metavar='<name>', help='User name')
350
 
    @args('--access', dest="access", metavar='<access>', help='Access')
351
 
    @args('--secret', dest="secret", metavar='<secret>', help='Secret')
352
259
    def create(self, name, access=None, secret=None):
353
 
        """creates a new user and prints exports"""
354
 
        try:
355
 
            user = self.manager.create_user(name, access, secret, False)
356
 
        except exception.DBError, e:
357
 
            _db_error(e)
 
260
        """creates a new user and prints exports
 
261
        arguments: name [access] [secret]"""
 
262
        user = self.manager.create_user(name, access, secret, False)
358
263
        self._print_export(user)
359
264
 
360
 
    @args('--name', dest="name", metavar='<name>', help='User name')
361
265
    def delete(self, name):
362
266
        """deletes an existing user
363
267
        arguments: name"""
364
268
        self.manager.delete_user(name)
365
269
 
366
 
    @args('--name', dest="name", metavar='<admin name>', help='User name')
367
270
    def exports(self, name):
368
 
        """prints access and secrets for user in export format"""
 
271
        """prints access and secrets for user in export format
 
272
        arguments: name"""
369
273
        user = self.manager.get_user(name)
370
274
        if user:
371
275
            self._print_export(user)
373
277
            print "User %s doesn't exist" % name
374
278
 
375
279
    def list(self):
376
 
        """lists all users"""
 
280
        """lists all users
 
281
        arguments: <none>"""
377
282
        for user in self.manager.get_users():
378
283
            print user.name
379
284
 
380
 
    @args('--name', dest="name", metavar='<name>', help='User name')
381
 
    @args('--access', dest="access_key", metavar='<access>',
382
 
            help='Access key')
383
 
    @args('--secret', dest="secret_key", metavar='<secret>',
384
 
            help='Secret key')
385
 
    @args('--is_admin', dest='is_admin', metavar="<'T'|'F'>",
386
 
            help='Is admin?')
387
285
    def modify(self, name, access_key, secret_key, is_admin):
388
286
        """update a users keys & admin flag
389
287
        arguments: accesskey secretkey admin
397
295
            is_admin = False
398
296
        self.manager.modify_user(name, access_key, secret_key, is_admin)
399
297
 
400
 
    @args('--name', dest="user_id", metavar='<name>', help='User name')
401
 
    @args('--project', dest="project_id", metavar='<Project name>',
402
 
            help='Project name')
403
 
    def revoke(self, user_id, project_id=None):
404
 
        """revoke certs for a user"""
405
 
        if project_id:
406
 
            crypto.revoke_certs_by_user_and_project(user_id, project_id)
407
 
        else:
408
 
            crypto.revoke_certs_by_user(user_id)
409
 
 
410
298
 
411
299
class ProjectCommands(object):
412
300
    """Class for managing projects."""
414
302
    def __init__(self):
415
303
        self.manager = manager.AuthManager()
416
304
 
417
 
    @args('--project', dest="project_id", metavar='<Project name>',
418
 
            help='Project name')
419
 
    @args('--user', dest="user_id", metavar='<name>', help='User name')
420
305
    def add(self, project_id, user_id):
421
 
        """Adds user to project"""
422
 
        try:
423
 
            self.manager.add_to_project(user_id, project_id)
424
 
        except exception.UserNotFound as ex:
425
 
            print ex
426
 
            raise
 
306
        """Adds user to project
 
307
        arguments: project_id user_id"""
 
308
        self.manager.add_to_project(user_id, project_id)
427
309
 
428
 
    @args('--project', dest="name", metavar='<Project name>',
429
 
            help='Project name')
430
 
    @args('--user', dest="project_manager", metavar='<user>',
431
 
            help='Project manager')
432
 
    @args('--desc', dest="description", metavar='<description>',
433
 
            help='Description')
434
310
    def create(self, name, project_manager, description=None):
435
 
        """Creates a new project"""
436
 
        try:
437
 
            self.manager.create_project(name, project_manager, description)
438
 
        except exception.UserNotFound as ex:
439
 
            print ex
440
 
            raise
441
 
 
442
 
    @args('--project', dest="name", metavar='<Project name>',
443
 
            help='Project name')
444
 
    @args('--user', dest="project_manager", metavar='<user>',
445
 
            help='Project manager')
446
 
    @args('--desc', dest="description", metavar='<description>',
447
 
            help='Description')
448
 
    def modify(self, name, project_manager, description=None):
449
 
        """Modifies a project"""
450
 
        try:
451
 
            self.manager.modify_project(name, project_manager, description)
452
 
        except exception.UserNotFound as ex:
453
 
            print ex
454
 
            raise
455
 
 
456
 
    @args('--project', dest="name", metavar='<Project name>',
457
 
            help='Project name')
 
311
        """Creates a new project
 
312
        arguments: name project_manager [description]"""
 
313
        self.manager.create_project(name, project_manager, description)
 
314
 
458
315
    def delete(self, name):
459
 
        """Deletes an existing project"""
460
 
        try:
461
 
            self.manager.delete_project(name)
462
 
        except exception.ProjectNotFound as ex:
463
 
            print ex
464
 
            raise
 
316
        """Deletes an existing project
 
317
        arguments: name"""
 
318
        self.manager.delete_project(name)
465
319
 
466
 
    @args('--project', dest="project_id", metavar='<Project name>',
467
 
            help='Project name')
468
 
    @args('--user', dest="user_id", metavar='<name>', help='User name')
469
 
    @args('--file', dest="filename", metavar='<filename>',
470
 
            help='File name(Default: novarc)')
471
320
    def environment(self, project_id, user_id, filename='novarc'):
472
 
        """Exports environment variables to an sourcable file"""
473
 
        try:
474
 
            rc = self.manager.get_environment_rc(user_id, project_id)
475
 
        except (exception.UserNotFound, exception.ProjectNotFound) as ex:
476
 
            print ex
477
 
            raise
478
 
        if filename == "-":
479
 
            sys.stdout.write(rc)
480
 
        else:
481
 
            with open(filename, 'w') as f:
482
 
                f.write(rc)
 
321
        """Exports environment variables to an sourcable file
 
322
        arguments: project_id user_id [filename='novarc]"""
 
323
        rc = self.manager.get_environment_rc(user_id, project_id)
 
324
        with open(filename, 'w') as f:
 
325
            f.write(rc)
483
326
 
484
 
    @args('--user', dest="username", metavar='<username>', help='User name')
485
 
    def list(self, username=None):
486
 
        """Lists all projects"""
487
 
        for project in self.manager.get_projects(username):
 
327
    def list(self):
 
328
        """Lists all projects
 
329
        arguments: <none>"""
 
330
        for project in self.manager.get_projects():
488
331
            print project.name
489
332
 
490
 
    @args('--project', dest="project_id", metavar='<Project name>',
491
 
            help='Project name')
492
 
    @args('--key', dest="key", metavar='<key>', help='Key')
493
 
    @args('--value', dest="value", metavar='<value>', help='Value')
494
333
    def quota(self, project_id, key=None, value=None):
495
 
        """Set or display quotas for project"""
 
334
        """Set or display quotas for project
 
335
        arguments: project_id [key] [value]"""
496
336
        ctxt = context.get_admin_context()
497
337
        if key:
498
 
            if value.lower() == 'unlimited':
499
 
                value = None
 
338
            quo = {'project_id': project_id, key: value}
500
339
            try:
501
 
                db.quota_update(ctxt, project_id, key, value)
502
 
            except exception.ProjectQuotaNotFound:
503
 
                db.quota_create(ctxt, project_id, key, value)
504
 
        project_quota = quota.get_project_quotas(ctxt, project_id)
 
340
                db.quota_update(ctxt, project_id, quo)
 
341
            except exception.NotFound:
 
342
                db.quota_create(ctxt, quo)
 
343
        project_quota = quota.get_quota(ctxt, project_id)
505
344
        for key, value in project_quota.iteritems():
506
 
            if value is None:
507
 
                value = 'unlimited'
508
345
            print '%s: %s' % (key, value)
509
346
 
510
 
    @args('--project', dest="project_id", metavar='<Project name>',
511
 
            help='Project name')
512
 
    @args('--user', dest="user_id", metavar='<name>', help='User name')
513
347
    def remove(self, project_id, user_id):
514
 
        """Removes user from project"""
515
 
        try:
516
 
            self.manager.remove_from_project(user_id, project_id)
517
 
        except (exception.UserNotFound, exception.ProjectNotFound) as ex:
518
 
            print ex
519
 
            raise
 
348
        """Removes user from project
 
349
        arguments: project_id user_id"""
 
350
        self.manager.remove_from_project(user_id, project_id)
520
351
 
521
 
    @args('--project', dest="project_id", metavar='<Project name>',
522
 
            help='Project name')
523
352
    def scrub(self, project_id):
524
 
        """Deletes data associated with project"""
525
 
        admin_context = context.get_admin_context()
526
 
        networks = db.project_get_networks(admin_context, project_id)
527
 
        for network in networks:
528
 
            db.network_disassociate(admin_context, network['id'])
529
 
        groups = db.security_group_get_by_project(admin_context, project_id)
 
353
        """Deletes data associated with project
 
354
        arguments: project_id"""
 
355
        ctxt = context.get_admin_context()
 
356
        network_ref = db.project_get_network(ctxt, project_id)
 
357
        db.network_disassociate(ctxt, network_ref['id'])
 
358
        groups = db.security_group_get_by_project(ctxt, project_id)
530
359
        for group in groups:
531
 
            db.security_group_destroy(admin_context, group['id'])
 
360
            db.security_group_destroy(ctxt, group['id'])
532
361
 
533
 
    @args('--project', dest="project_id", metavar='<Project name>',
534
 
            help='Project name')
535
 
    @args('--user', dest="user_id", metavar='<name>', help='User name')
536
 
    @args('--file', dest="filename", metavar='<filename>',
537
 
            help='File name(Default: nova.zip)')
538
362
    def zipfile(self, project_id, user_id, filename='nova.zip'):
539
 
        """Exports credentials for project to a zip file"""
 
363
        """Exports credentials for project to a zip file
 
364
        arguments: project_id user_id [filename='nova.zip]"""
540
365
        try:
541
366
            zip_file = self.manager.get_credentials(user_id, project_id)
542
 
            if filename == "-":
543
 
                sys.stdout.write(zip_file)
544
 
            else:
545
 
                with open(filename, 'w') as f:
546
 
                    f.write(zip_file)
547
 
        except (exception.UserNotFound, exception.ProjectNotFound) as ex:
548
 
            print ex
549
 
            raise
 
367
            with open(filename, 'w') as f:
 
368
                f.write(zip_file)
550
369
        except db.api.NoMoreNetworks:
551
 
            print _('No more networks available. If this is a new '
552
 
                    'installation, you need\nto call something like this:\n\n'
553
 
                    '  nova-manage network create pvt 10.0.0.0/8 10 64\n\n')
554
 
        except exception.ProcessExecutionError, e:
555
 
            print e
556
 
            print _("The above error may show that the certificate db has "
557
 
                    "not been created.\nPlease create a database by running "
558
 
                    "a nova-api server on this host.")
559
 
 
560
 
AccountCommands = ProjectCommands
561
 
 
562
 
 
563
 
class FixedIpCommands(object):
564
 
    """Class for managing fixed ip."""
565
 
 
566
 
    @args('--host', dest="host", metavar='<host>', help='Host')
567
 
    def list(self, host=None):
568
 
        """Lists all fixed ips (optionally by host)"""
569
 
        ctxt = context.get_admin_context()
570
 
 
571
 
        try:
572
 
            if host is None:
573
 
                fixed_ips = db.fixed_ip_get_all(ctxt)
574
 
            else:
575
 
                fixed_ips = db.fixed_ip_get_all_by_instance_host(ctxt, host)
576
 
        except exception.NotFound as ex:
577
 
            print "error: %s" % ex
578
 
            sys.exit(2)
579
 
 
580
 
        print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'),
581
 
                                                  _('IP address'),
582
 
                                                  _('MAC address'),
583
 
                                                  _('hostname'),
584
 
                                                  _('host'))
585
 
        for fixed_ip in fixed_ips:
586
 
            hostname = None
587
 
            host = None
588
 
            mac_address = None
589
 
            if fixed_ip['instance']:
590
 
                instance = fixed_ip['instance']
591
 
                hostname = instance['hostname']
592
 
                host = instance['host']
593
 
                mac_address = fixed_ip['virtual_interface']['address']
594
 
            print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (
595
 
                    fixed_ip['network']['cidr'],
596
 
                    fixed_ip['address'],
597
 
                    mac_address, hostname, host)
598
 
 
599
 
    @args('--address', dest="address", metavar='<ip address>',
600
 
          help='IP address')
601
 
    def reserve(self, address):
602
 
        """Mark fixed ip as reserved
603
 
        arguments: address"""
604
 
        self._set_reserved(address, True)
605
 
 
606
 
    @args('--address', dest="address", metavar='<ip address>',
607
 
          help='IP address')
608
 
    def unreserve(self, address):
609
 
        """Mark fixed ip as free to use
610
 
        arguments: address"""
611
 
        self._set_reserved(address, False)
612
 
 
613
 
    def _set_reserved(self, address, reserved):
614
 
        ctxt = context.get_admin_context()
615
 
 
616
 
        try:
617
 
            fixed_ip = db.fixed_ip_get_by_address(ctxt, address)
618
 
            if fixed_ip is None:
619
 
                raise exception.NotFound('Could not find address')
620
 
            db.fixed_ip_update(ctxt, fixed_ip['address'],
621
 
                                {'reserved': reserved})
622
 
        except exception.NotFound as ex:
623
 
            print "error: %s" % ex
624
 
            sys.exit(2)
 
370
            print ('No more networks available. If this is a new '
 
371
                   'installation, you need\nto call something like this:\n\n'
 
372
                   '    nova-manage network create 10.0.0.0/8 10 64\n\n')
625
373
 
626
374
 
627
375
class FloatingIpCommands(object):
628
376
    """Class for managing floating ip."""
629
377
 
630
 
    @args('--ip_range', dest="range", metavar='<range>', help='IP range')
631
 
    def create(self, range):
632
 
        """Creates floating ips for zone by range"""
633
 
        for address in netaddr.IPNetwork(range):
 
378
    def create(self, host, range):
 
379
        """Creates floating ips for host by range
 
380
        arguments: host ip_range"""
 
381
        for address in IPy.IP(range):
634
382
            db.floating_ip_create(context.get_admin_context(),
635
 
                                  {'address': str(address)})
 
383
                                  {'address': str(address),
 
384
                                   'host': host})
636
385
 
637
 
    @args('--ip_range', dest="ip_range", metavar='<range>', help='IP range')
638
386
    def delete(self, ip_range):
639
 
        """Deletes floating ips by range"""
640
 
        for address in netaddr.IPNetwork(ip_range):
 
387
        """Deletes floating ips by range
 
388
        arguments: range"""
 
389
        for address in IPy.IP(ip_range):
641
390
            db.floating_ip_destroy(context.get_admin_context(),
642
391
                                   str(address))
643
392
 
644
 
    @args('--host', dest="host", metavar='<host>', help='Host')
645
393
    def list(self, host=None):
646
394
        """Lists all floating ips (optionally by host)
647
 
        Note: if host is given, only active floating IPs are returned"""
 
395
        arguments: [host]"""
648
396
        ctxt = context.get_admin_context()
649
 
        if host is None:
 
397
        if host == None:
650
398
            floating_ips = db.floating_ip_get_all(ctxt)
651
399
        else:
652
400
            floating_ips = db.floating_ip_get_all_by_host(ctxt, host)
653
401
        for floating_ip in floating_ips:
654
402
            instance = None
655
403
            if floating_ip['fixed_ip']:
656
 
                instance = floating_ip['fixed_ip']['instance']['hostname']
 
404
                instance = floating_ip['fixed_ip']['instance']['ec2_id']
657
405
            print "%s\t%s\t%s" % (floating_ip['host'],
658
406
                                  floating_ip['address'],
659
407
                                  instance)
662
410
class NetworkCommands(object):
663
411
    """Class for managing networks."""
664
412
 
665
 
    @args('--label', dest="label", metavar='<label>',
666
 
            help='Label for network (ex: public)')
667
 
    @args('--fixed_range_v4', dest="fixed_range_v4", metavar='<x.x.x.x/yy>',
668
 
            help='IPv4 subnet (ex: 10.0.0.0/8)')
669
 
    @args('--num_networks', dest="num_networks", metavar='<number>',
670
 
            help='Number of networks to create')
671
 
    @args('--network_size', dest="network_size", metavar='<number>',
672
 
            help='Number of IPs per network')
673
 
    @args('--vlan', dest="vlan_start", metavar='<vlan id>', help='vlan id')
674
 
    @args('--vpn', dest="vpn_start", help='vpn start')
675
 
    @args('--fixed_range_v6', dest="fixed_range_v6",
676
 
          help='IPv6 subnet (ex: fe80::/64')
677
 
    @args('--gateway_v6', dest="gateway_v6", help='ipv6 gateway')
678
 
    @args('--bridge', dest="bridge",
679
 
            metavar='<bridge>',
680
 
            help='VIFs on this network are connected to this bridge')
681
 
    @args('--bridge_interface', dest="bridge_interface",
682
 
            metavar='<bridge interface>',
683
 
            help='the bridge is connected to this interface')
684
 
    @args('--multi_host', dest="multi_host", metavar="<'T'|'F'>",
685
 
            help='Multi host')
686
 
    @args('--dns1', dest="dns1", metavar="<DNS Address>", help='First DNS')
687
 
    @args('--dns2', dest="dns2", metavar="<DNS Address>", help='Second DNS')
688
 
    @args('--uuid', dest="net_uuid", metavar="<network uuid>",
689
 
      help='Network UUID')
690
 
    @args('--project_id', dest="project_id", metavar="<project id>",
691
 
      help='Project id')
692
 
    @args('--priority', dest="priority", metavar="<number>",
693
 
      help='Network interface priority')
694
 
    def create(self, label=None, fixed_range_v4=None, num_networks=None,
695
 
               network_size=None, multi_host=None, vlan_start=None,
696
 
               vpn_start=None, fixed_range_v6=None, gateway_v6=None,
697
 
               bridge=None, bridge_interface=None, dns1=None, dns2=None,
698
 
               project_id=None, priority=None, uuid=None):
699
 
        """Creates fixed ips for host by range"""
700
 
 
701
 
        # check for certain required inputs
702
 
        if not label:
703
 
            raise exception.NetworkNotCreated(req='--label')
704
 
        if not (fixed_range_v4 or fixed_range_v6):
705
 
            req = '--fixed_range_v4 or --fixed_range_v6'
706
 
            raise exception.NetworkNotCreated(req=req)
707
 
 
708
 
        bridge = bridge or FLAGS.flat_network_bridge
709
 
        if not bridge:
710
 
            bridge_required = ['nova.network.manager.FlatManager',
711
 
                               'nova.network.manager.FlatDHCPManager']
712
 
            if FLAGS.network_manager in bridge_required:
713
 
                # TODO(tr3buchet) - swap print statement and following line for
714
 
                #                   raise statement in diablo 4
715
 
                print _('--bridge parameter required or FLAG '
716
 
                        'flat_network_bridge must be set to create networks\n'
717
 
                        'WARNING! ACHTUNG! Setting the bridge to br100 '
718
 
                        'automatically is deprecated and will be removed in '
719
 
                        'Diablo milestone 4. Prepare yourself accordingly.')
720
 
                time.sleep(10)
721
 
                bridge = 'br100'
722
 
                #raise exception.NetworkNotCreated(req='--bridge')
723
 
 
724
 
        bridge_interface = bridge_interface or FLAGS.flat_interface or \
725
 
                           FLAGS.vlan_interface
726
 
        if not bridge_interface:
727
 
            interface_required = ['nova.network.manager.VlanManager']
728
 
            if FLAGS.network_manager in interface_required:
729
 
                raise exception.NetworkNotCreated(req='--bridge_interface')
730
 
 
731
 
        # sanitize other input using FLAGS if necessary
 
413
    def create(self, fixed_range=None, num_networks=None,
 
414
               network_size=None, vlan_start=None, vpn_start=None,fixed_range_v6=None):
 
415
        """Creates fixed ips for host by range
 
416
        arguments: [fixed_range=FLAG], [num_networks=FLAG],
 
417
                   [network_size=FLAG], [vlan_start=FLAG],
 
418
                   [vpn_start=FLAG],[fixed_range_v6=FLAG]"""
 
419
        if not fixed_range:
 
420
            fixed_range = FLAGS.fixed_range
732
421
        if not num_networks:
733
422
            num_networks = FLAGS.num_networks
734
 
        if not network_size and fixed_range_v4:
735
 
            fixnet = netaddr.IPNetwork(fixed_range_v4)
736
 
            each_subnet_size = fixnet.size / int(num_networks)
737
 
            if each_subnet_size > FLAGS.network_size:
738
 
                network_size = FLAGS.network_size
739
 
                subnet = 32 - int(math.log(network_size, 2))
740
 
                oversize_msg = _('Subnet(s) too large, defaulting to /%s.'
741
 
                         '  To override, specify network_size flag.') % subnet
742
 
                print oversize_msg
743
 
            else:
744
 
                network_size = fixnet.size
745
 
        if not multi_host:
746
 
            multi_host = FLAGS.multi_host
747
 
        else:
748
 
            multi_host = multi_host == 'T'
 
423
        if not network_size:
 
424
            network_size = FLAGS.network_size
749
425
        if not vlan_start:
750
426
            vlan_start = FLAGS.vlan_start
751
427
        if not vpn_start:
752
428
            vpn_start = FLAGS.vpn_start
753
 
        if not dns1 and FLAGS.flat_network_dns:
754
 
            dns1 = FLAGS.flat_network_dns
755
 
 
756
 
        if not network_size:
757
 
            network_size = FLAGS.network_size
758
 
 
759
 
        # create the network
 
429
        if not fixed_range_v6:
 
430
            fixed_range_v6 = FLAGS.fixed_range_v6
760
431
        net_manager = utils.import_object(FLAGS.network_manager)
761
432
        net_manager.create_networks(context.get_admin_context(),
762
 
                                    label=label,
763
 
                                    cidr=fixed_range_v4,
764
 
                                    multi_host=multi_host,
765
 
                                    num_networks=int(num_networks),
766
 
                                    network_size=int(network_size),
767
 
                                    vlan_start=int(vlan_start),
768
 
                                    vpn_start=int(vpn_start),
769
 
                                    cidr_v6=fixed_range_v6,
770
 
                                    gateway_v6=gateway_v6,
771
 
                                    bridge=bridge,
772
 
                                    bridge_interface=bridge_interface,
773
 
                                    dns1=dns1,
774
 
                                    dns2=dns2,
775
 
                                    project_id=project_id,
776
 
                                    priority=priority,
777
 
                                    uuid=uuid)
778
 
 
779
 
    def list(self):
780
 
        """List all created networks"""
781
 
        _fmt = "%-5s\t%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s"
782
 
        print _fmt % (_('id'),
783
 
                          _('IPv4'),
784
 
                          _('IPv6'),
785
 
                          _('start address'),
786
 
                          _('DNS1'),
787
 
                          _('DNS2'),
788
 
                          _('VlanID'),
789
 
                          _('project'),
790
 
                          _("uuid"))
791
 
        for network in db.network_get_all(context.get_admin_context()):
792
 
            print _fmt % (network.id,
793
 
                          network.cidr,
794
 
                          network.cidr_v6,
795
 
                          network.dhcp_start,
796
 
                          network.dns1,
797
 
                          network.dns2,
798
 
                          network.vlan,
799
 
                          network.project_id,
800
 
                          network.uuid)
801
 
 
802
 
    def quantum_list(self):
803
 
        """List all created networks with Quantum-relevant fields"""
804
 
        _fmt = "%-32s\t%-10s\t%-10s\t%s , %s"
805
 
        print _fmt % (_('uuid'),
806
 
                      _('project'),
807
 
                      _('priority'),
808
 
                      _('cidr_v4'),
809
 
                      _('cidr_v6'))
810
 
        for network in db.network_get_all(context.get_admin_context()):
811
 
            print _fmt % (network.uuid,
812
 
                          network.project_id,
813
 
                          network.priority,
814
 
                          network.cidr,
815
 
                          network.cidr_v6)
816
 
 
817
 
    @args('--network', dest="fixed_range", metavar='<x.x.x.x/yy>',
818
 
            help='Network to delete')
819
 
    def delete(self, fixed_range):
820
 
        """Deletes a network"""
821
 
 
822
 
        # delete the network
823
 
        net_manager = utils.import_object(FLAGS.network_manager)
824
 
        net_manager.delete_network(context.get_admin_context(), fixed_range)
825
 
 
826
 
    @args('--network', dest="fixed_range", metavar='<x.x.x.x/yy>',
827
 
            help='Network to modify')
828
 
    @args('--project', dest="project", metavar='<project name>',
829
 
            help='Project name to associate')
830
 
    @args('--host', dest="host", metavar='<host>',
831
 
            help='Host to associate')
832
 
    @args('--disassociate-project', action="store_true", dest='dis_project',
833
 
          default=False, help='Disassociate Network from Project')
834
 
    @args('--disassociate-host', action="store_true", dest='dis_host',
835
 
          default=False, help='Disassociate Host from Project')
836
 
    def modify(self, fixed_range, project=None, host=None,
837
 
               dis_project=None, dis_host=None):
838
 
        """Associate/Disassociate Network with Project and/or Host
839
 
        arguments: network project host
840
 
        leave any field blank to ignore it
841
 
        """
842
 
        admin_context = context.get_admin_context()
843
 
        network = db.network_get_by_cidr(admin_context, fixed_range)
844
 
        net = {}
845
 
        #User can choose the following actions each for project and host.
846
 
        #1) Associate (set not None value given by project/host parameter)
847
 
        #2) Disassociate (set None by disassociate parameter)
848
 
        #3) Keep unchanged (project/host key is not added to 'net')
849
 
        if project:
850
 
            net['project_id'] = project
851
 
        elif dis_project:
852
 
            net['project_id'] = None
853
 
        if host:
854
 
            net['host'] = host
855
 
        elif dis_host:
856
 
            net['host'] = None
857
 
        db.network_update(admin_context, network['id'], net)
858
 
 
859
 
 
860
 
class VmCommands(object):
861
 
    """Class for mangaging VM instances."""
862
 
 
863
 
    @args('--host', dest="host", metavar='<host>', help='Host')
864
 
    def list(self, host=None):
865
 
        """Show a list of all instances"""
866
 
 
867
 
        print "%-10s %-15s %-10s %-10s %-26s %-9s %-9s %-9s" \
868
 
              "  %-10s %-10s %-10s %-5s" % (
869
 
            _('instance'),
870
 
            _('node'),
871
 
            _('type'),
872
 
            _('state'),
873
 
            _('launched'),
874
 
            _('image'),
875
 
            _('kernel'),
876
 
            _('ramdisk'),
877
 
            _('project'),
878
 
            _('user'),
879
 
            _('zone'),
880
 
            _('index'))
881
 
 
882
 
        if host is None:
883
 
            instances = db.instance_get_all(context.get_admin_context())
884
 
        else:
885
 
            instances = db.instance_get_all_by_host(
886
 
                           context.get_admin_context(), host)
887
 
 
888
 
        for instance in instances:
889
 
            print "%-10s %-15s %-10s %-10s %-26s %-9s %-9s %-9s" \
890
 
                  "  %-10s %-10s %-10s %-5d" % (
891
 
                instance['hostname'],
892
 
                instance['host'],
893
 
                instance['instance_type'].name,
894
 
                instance['vm_state'],
895
 
                instance['launched_at'],
896
 
                instance['image_ref'],
897
 
                instance['kernel_id'],
898
 
                instance['ramdisk_id'],
899
 
                instance['project_id'],
900
 
                instance['user_id'],
901
 
                instance['availability_zone'],
902
 
                instance['launch_index'])
903
 
 
904
 
    def _migration(self, ec2_id, dest, block_migration=False):
905
 
        """Migrates a running instance to a new machine.
906
 
         :param ec2_id: instance id which comes from euca-describe-instance.
907
 
         :param dest: destination host name.
908
 
         :param block_migration: if True, do block_migration.
909
 
 
910
 
        """
911
 
 
912
 
        ctxt = context.get_admin_context()
913
 
        instance_id = ec2utils.ec2_id_to_id(ec2_id)
914
 
 
915
 
        if (FLAGS.connection_type != 'libvirt' or
916
 
           (FLAGS.connection_type == 'libvirt' and
917
 
            FLAGS.libvirt_type not in ['kvm', 'qemu'])):
918
 
            msg = _('Only KVM and QEmu are supported for now. Sorry!')
919
 
            raise exception.Error(msg)
920
 
 
921
 
        if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
922
 
            FLAGS.volume_driver != 'nova.volume.driver.ISCSIDriver'):
923
 
            msg = _("Support only AOEDriver and ISCSIDriver. Sorry!")
924
 
            raise exception.Error(msg)
925
 
 
926
 
        rpc.call(ctxt,
927
 
                 FLAGS.scheduler_topic,
928
 
                 {"method": "live_migration",
929
 
                  "args": {"instance_id": instance_id,
930
 
                           "dest": dest,
931
 
                           "topic": FLAGS.compute_topic,
932
 
                           "block_migration": block_migration}})
933
 
 
934
 
        print _('Migration of %s initiated.'
935
 
               'Check its progress using euca-describe-instances.') % ec2_id
936
 
 
937
 
    @args('--ec2_id', dest='ec2_id', metavar='<ec2 id>', help='EC2 ID')
938
 
    @args('--dest', dest='dest', metavar='<Destanation>',
939
 
            help='destanation node')
940
 
    def live_migration(self, ec2_id, dest):
941
 
        """Migrates a running instance to a new machine."""
942
 
 
943
 
        self._migration(ec2_id, dest)
944
 
 
945
 
    @args('--ec2_id', dest='ec2_id', metavar='<ec2 id>', help='EC2 ID')
946
 
    @args('--dest', dest='dest', metavar='<Destanation>',
947
 
            help='destanation node')
948
 
    def block_migration(self, ec2_id, dest):
949
 
        """Migrates a running instance to a new machine with storage data."""
950
 
 
951
 
        self._migration(ec2_id, dest, True)
952
 
 
953
 
 
954
 
class ServiceCommands(object):
955
 
    """Enable and disable running services"""
956
 
 
957
 
    @args('--host', dest='host', metavar='<host>', help='Host')
958
 
    @args('--service', dest='service', metavar='<service>',
959
 
            help='Nova service')
960
 
    def list(self, host=None, service=None):
961
 
        """
962
 
        Show a list of all running services. Filter by host & service name.
963
 
        """
964
 
        ctxt = context.get_admin_context()
965
 
        now = utils.utcnow()
966
 
        services = db.service_get_all(ctxt)
967
 
        if host:
968
 
            services = [s for s in services if s['host'] == host]
969
 
        if service:
970
 
            services = [s for s in services if s['binary'] == service]
971
 
        print_format = "%-16s %-36s %-16s %-10s %-5s %-10s"
972
 
        print print_format % (
973
 
                    _('Binary'),
974
 
                    _('Host'),
975
 
                    _('Zone'),
976
 
                    _('Status'),
977
 
                    _('State'),
978
 
                    _('Updated_At'))
979
 
        for svc in services:
980
 
            delta = now - (svc['updated_at'] or svc['created_at'])
981
 
            alive = (delta.seconds <= 15)
982
 
            art = (alive and ":-)") or "XXX"
983
 
            active = 'enabled'
984
 
            if svc['disabled']:
985
 
                active = 'disabled'
986
 
            print print_format % (svc['binary'], svc['host'],
987
 
                                  svc['availability_zone'], active, art,
988
 
                                  svc['updated_at'])
989
 
 
990
 
    @args('--host', dest='host', metavar='<host>', help='Host')
991
 
    @args('--service', dest='service', metavar='<service>',
992
 
            help='Nova service')
993
 
    def enable(self, host, service):
994
 
        """Enable scheduling for a service"""
995
 
        ctxt = context.get_admin_context()
996
 
        svc = db.service_get_by_args(ctxt, host, service)
997
 
        if not svc:
998
 
            print "Unable to find service"
999
 
            return
1000
 
        db.service_update(ctxt, svc['id'], {'disabled': False})
1001
 
 
1002
 
    @args('--host', dest='host', metavar='<host>', help='Host')
1003
 
    @args('--service', dest='service', metavar='<service>',
1004
 
            help='Nova service')
1005
 
    def disable(self, host, service):
1006
 
        """Disable scheduling for a service"""
1007
 
        ctxt = context.get_admin_context()
1008
 
        svc = db.service_get_by_args(ctxt, host, service)
1009
 
        if not svc:
1010
 
            print "Unable to find service"
1011
 
            return
1012
 
        db.service_update(ctxt, svc['id'], {'disabled': True})
1013
 
 
1014
 
    @args('--host', dest='host', metavar='<host>', help='Host')
1015
 
    def describe_resource(self, host):
1016
 
        """Describes cpu/memory/hdd info for host."""
1017
 
 
1018
 
        result = rpc.call(context.get_admin_context(),
1019
 
                     FLAGS.scheduler_topic,
1020
 
                     {"method": "show_host_resources",
1021
 
                      "args": {"host": host}})
1022
 
 
1023
 
        if type(result) != dict:
1024
 
            print _('An unexpected error has occurred.')
1025
 
            print _('[Result]'), result
1026
 
        else:
1027
 
            cpu = result['resource']['vcpus']
1028
 
            mem = result['resource']['memory_mb']
1029
 
            hdd = result['resource']['local_gb']
1030
 
            cpu_u = result['resource']['vcpus_used']
1031
 
            mem_u = result['resource']['memory_mb_used']
1032
 
            hdd_u = result['resource']['local_gb_used']
1033
 
 
1034
 
            cpu_sum = 0
1035
 
            mem_sum = 0
1036
 
            hdd_sum = 0
1037
 
            print 'HOST\t\t\tPROJECT\t\tcpu\tmem(mb)\tdisk(gb)'
1038
 
            print '%s(total)\t\t\t%s\t%s\t%s' % (host, cpu, mem, hdd)
1039
 
            print '%s(used_now)\t\t\t%s\t%s\t%s' % (host, cpu_u, mem_u, hdd_u)
1040
 
            for p_id, val in result['usage'].items():
1041
 
                cpu_sum += val['vcpus']
1042
 
                mem_sum += val['memory_mb']
1043
 
                hdd_sum += val['local_gb']
1044
 
            print '%s(used_max)\t\t\t%s\t%s\t%s' % (host, cpu_sum,
1045
 
                                                    mem_sum, hdd_sum)
1046
 
 
1047
 
            for p_id, val in result['usage'].items():
1048
 
                print '%s\t\t%s\t\t%s\t%s\t%s' % (host,
1049
 
                                                  p_id,
1050
 
                                                  val['vcpus'],
1051
 
                                                  val['memory_mb'],
1052
 
                                                  val['local_gb'])
1053
 
 
1054
 
    @args('--host', dest='host', metavar='<host>', help='Host')
1055
 
    def update_resource(self, host):
1056
 
        """Updates available vcpu/memory/disk info for host."""
1057
 
 
1058
 
        ctxt = context.get_admin_context()
1059
 
        service_refs = db.service_get_all_by_host(ctxt, host)
1060
 
        if len(service_refs) <= 0:
1061
 
            raise exception.Invalid(_('%s does not exist.') % host)
1062
 
 
1063
 
        service_refs = [s for s in service_refs if s['topic'] == 'compute']
1064
 
        if len(service_refs) <= 0:
1065
 
            raise exception.Invalid(_('%s is not compute node.') % host)
1066
 
 
1067
 
        rpc.call(ctxt,
1068
 
                 db.queue_get_for(ctxt, FLAGS.compute_topic, host),
1069
 
                 {"method": "update_available_resource"})
1070
 
 
1071
 
 
1072
 
class HostCommands(object):
1073
 
    """List hosts"""
1074
 
 
1075
 
    def list(self, zone=None):
1076
 
        """Show a list of all physical hosts. Filter by zone.
1077
 
        args: [zone]"""
1078
 
        print "%-25s\t%-15s" % (_('host'),
1079
 
                                _('zone'))
1080
 
        ctxt = context.get_admin_context()
1081
 
        now = utils.utcnow()
1082
 
        services = db.service_get_all(ctxt)
1083
 
        if zone:
1084
 
            services = [s for s in services if s['availability_zone'] == zone]
1085
 
        hosts = []
1086
 
        for srv in services:
1087
 
            if not [h for h in hosts if h['host'] == srv['host']]:
1088
 
                hosts.append(srv)
1089
 
 
1090
 
        for h in hosts:
1091
 
            print "%-25s\t%-15s" % (h['host'], h['availability_zone'])
1092
 
 
1093
 
 
1094
 
class DbCommands(object):
1095
 
    """Class for managing the database."""
1096
 
 
1097
 
    def __init__(self):
1098
 
        pass
1099
 
 
1100
 
    @args('--version', dest='version', metavar='<version>',
1101
 
            help='Database version')
1102
 
    def sync(self, version=None):
1103
 
        """Sync the database up to the most recent version."""
1104
 
        return migration.db_sync(version)
1105
 
 
1106
 
    def version(self):
1107
 
        """Print the current database version."""
1108
 
        print migration.db_version()
1109
 
 
1110
 
 
1111
 
class VersionCommands(object):
1112
 
    """Class for exposing the codebase version."""
1113
 
 
1114
 
    def __init__(self):
1115
 
        pass
1116
 
 
1117
 
    def list(self):
1118
 
        print _("%s (%s)") %\
1119
 
                (version.version_string(), version.version_string_with_vcs())
1120
 
 
1121
 
    def __call__(self):
1122
 
        self.list()
1123
 
 
1124
 
 
1125
 
class VsaCommands(object):
1126
 
    """Methods for dealing with VSAs"""
1127
 
 
1128
 
    def __init__(self, *args, **kwargs):
1129
 
        self.manager = manager.AuthManager()
1130
 
        self.vsa_api = vsa.API()
1131
 
        self.context = context.get_admin_context()
1132
 
 
1133
 
        self._format_str_vsa = "%(id)-5s %(vsa_id)-15s %(name)-25s "\
1134
 
                    "%(type)-10s %(vcs)-6s %(drives)-9s %(stat)-10s "\
1135
 
                    "%(az)-10s %(time)-10s"
1136
 
        self._format_str_volume = "\t%(id)-4s %(name)-15s %(size)-5s "\
1137
 
                    "%(stat)-10s %(att)-20s %(time)s"
1138
 
        self._format_str_drive = "\t%(id)-4s %(name)-15s %(size)-5s "\
1139
 
                    "%(stat)-10s %(host)-20s %(type)-4s %(tname)-10s %(time)s"
1140
 
        self._format_str_instance = "\t%(id)-4s %(name)-10s %(dname)-20s "\
1141
 
                    "%(image)-12s %(type)-10s %(fl_ip)-15s %(fx_ip)-15s "\
1142
 
                    "%(stat)-10s %(host)-15s %(time)s"
1143
 
 
1144
 
    def _print_vsa_header(self):
1145
 
        print self._format_str_vsa %\
1146
 
               dict(id=_('ID'),
1147
 
                    vsa_id=_('vsa_id'),
1148
 
                    name=_('displayName'),
1149
 
                    type=_('vc_type'),
1150
 
                    vcs=_('vc_cnt'),
1151
 
                    drives=_('drive_cnt'),
1152
 
                    stat=_('status'),
1153
 
                    az=_('AZ'),
1154
 
                    time=_('createTime'))
1155
 
 
1156
 
    def _print_vsa(self, vsa):
1157
 
        print self._format_str_vsa %\
1158
 
                dict(id=vsa['id'],
1159
 
                     vsa_id=vsa['name'],
1160
 
                     name=vsa['display_name'],
1161
 
                     type=vsa['vsa_instance_type'].get('name', None),
1162
 
                     vcs=vsa['vc_count'],
1163
 
                     drives=vsa['vol_count'],
1164
 
                     stat=vsa['status'],
1165
 
                     az=vsa['availability_zone'],
1166
 
                     time=str(vsa['created_at']))
1167
 
 
1168
 
    def _print_volume_header(self):
1169
 
        print _('      === Volumes ===')
1170
 
        print self._format_str_volume %\
1171
 
                dict(id=_('ID'),
1172
 
                     name=_('name'),
1173
 
                     size=_('size'),
1174
 
                     stat=_('status'),
1175
 
                     att=_('attachment'),
1176
 
                     time=_('createTime'))
1177
 
 
1178
 
    def _print_volume(self, vol):
1179
 
        print self._format_str_volume %\
1180
 
                dict(id=vol['id'],
1181
 
                     name=vol['display_name'] or vol['name'],
1182
 
                     size=vol['size'],
1183
 
                     stat=vol['status'],
1184
 
                     att=vol['attach_status'],
1185
 
                     time=str(vol['created_at']))
1186
 
 
1187
 
    def _print_drive_header(self):
1188
 
        print _('      === Drives ===')
1189
 
        print self._format_str_drive %\
1190
 
                dict(id=_('ID'),
1191
 
                    name=_('name'),
1192
 
                    size=_('size'),
1193
 
                    stat=_('status'),
1194
 
                    host=_('host'),
1195
 
                    type=_('type'),
1196
 
                    tname=_('typeName'),
1197
 
                    time=_('createTime'))
1198
 
 
1199
 
    def _print_drive(self, drive):
1200
 
        if drive['volume_type_id'] is not None and drive.get('volume_type'):
1201
 
            drive_type_name = drive['volume_type'].get('name')
1202
 
        else:
1203
 
            drive_type_name = ''
1204
 
 
1205
 
        print self._format_str_drive %\
1206
 
                dict(id=drive['id'],
1207
 
                    name=drive['display_name'],
1208
 
                    size=drive['size'],
1209
 
                    stat=drive['status'],
1210
 
                    host=drive['host'],
1211
 
                    type=drive['volume_type_id'],
1212
 
                    tname=drive_type_name,
1213
 
                    time=str(drive['created_at']))
1214
 
 
1215
 
    def _print_instance_header(self):
1216
 
        print _('      === Instances ===')
1217
 
        print self._format_str_instance %\
1218
 
                dict(id=_('ID'),
1219
 
                    name=_('name'),
1220
 
                    dname=_('disp_name'),
1221
 
                    image=_('image'),
1222
 
                    type=_('type'),
1223
 
                    fl_ip=_('floating_IP'),
1224
 
                    fx_ip=_('fixed_IP'),
1225
 
                    stat=_('status'),
1226
 
                    host=_('host'),
1227
 
                    time=_('createTime'))
1228
 
 
1229
 
    def _print_instance(self, vc):
1230
 
 
1231
 
        fixed_addr = None
1232
 
        floating_addr = None
1233
 
        if vc['fixed_ips']:
1234
 
            fixed = vc['fixed_ips'][0]
1235
 
            fixed_addr = fixed['address']
1236
 
            if fixed['floating_ips']:
1237
 
                floating_addr = fixed['floating_ips'][0]['address']
1238
 
        floating_addr = floating_addr or fixed_addr
1239
 
 
1240
 
        print self._format_str_instance %\
1241
 
                dict(id=vc['id'],
1242
 
                    name=ec2utils.id_to_ec2_id(vc['id']),
1243
 
                    dname=vc['display_name'],
1244
 
                    image=('ami-%08x' % int(vc['image_ref'])),
1245
 
                    type=vc['instance_type']['name'],
1246
 
                    fl_ip=floating_addr,
1247
 
                    fx_ip=fixed_addr,
1248
 
                    stat=vc['vm_state'],
1249
 
                    host=vc['host'],
1250
 
                    time=str(vc['created_at']))
1251
 
 
1252
 
    def _list(self, context, vsas, print_drives=False,
1253
 
                    print_volumes=False, print_instances=False):
1254
 
        if vsas:
1255
 
            self._print_vsa_header()
1256
 
 
1257
 
        for vsa in vsas:
1258
 
            self._print_vsa(vsa)
1259
 
            vsa_id = vsa.get('id')
1260
 
 
1261
 
            if print_instances:
1262
 
                instances = self.vsa_api.get_all_vsa_instances(context, vsa_id)
1263
 
                if instances:
1264
 
                    print
1265
 
                    self._print_instance_header()
1266
 
                    for instance in instances:
1267
 
                        self._print_instance(instance)
1268
 
                    print
1269
 
 
1270
 
            if print_drives:
1271
 
                drives = self.vsa_api.get_all_vsa_drives(context, vsa_id)
1272
 
                if drives:
1273
 
                    self._print_drive_header()
1274
 
                    for drive in drives:
1275
 
                        self._print_drive(drive)
1276
 
                    print
1277
 
 
1278
 
            if print_volumes:
1279
 
                volumes = self.vsa_api.get_all_vsa_volumes(context, vsa_id)
1280
 
                if volumes:
1281
 
                    self._print_volume_header()
1282
 
                    for volume in volumes:
1283
 
                        self._print_volume(volume)
1284
 
                    print
1285
 
 
1286
 
    @args('--storage', dest='storage',
1287
 
        metavar="[{'drive_name': 'type', 'num_drives': N, 'size': M},..]",
1288
 
        help='Initial storage allocation for VSA')
1289
 
    @args('--name', dest='name', metavar="<name>", help='VSA name')
1290
 
    @args('--description', dest='description', metavar="<description>",
1291
 
        help='VSA description')
1292
 
    @args('--vc', dest='vc_count', metavar="<number>", help='Number of VCs')
1293
 
    @args('--instance_type', dest='instance_type_name', metavar="<name>",
1294
 
        help='Instance type name')
1295
 
    @args('--image', dest='image_name', metavar="<name>", help='Image name')
1296
 
    @args('--shared', dest='shared', action="store_true", default=False,
1297
 
        help='Use shared drives')
1298
 
    @args('--az', dest='az', metavar="<zone:host>", help='Availability zone')
1299
 
    @args('--user', dest="user_id", metavar='<User name>',
1300
 
            help='User name')
1301
 
    @args('--project', dest="project_id", metavar='<Project name>',
1302
 
            help='Project name')
1303
 
    def create(self, storage='[]', name=None, description=None, vc_count=1,
1304
 
                     instance_type_name=None, image_name=None, shared=None,
1305
 
                     az=None, user_id=None, project_id=None):
1306
 
        """Create a VSA."""
1307
 
 
1308
 
        if project_id is None:
1309
 
            try:
1310
 
                project_id = os.getenv("EC2_ACCESS_KEY").split(':')[1]
1311
 
            except Exception as exc:
1312
 
                print _("Failed to retrieve project id: %(exc)s") % exc
1313
 
                raise
1314
 
 
1315
 
        if user_id is None:
1316
 
            try:
1317
 
                project = self.manager.get_project(project_id)
1318
 
                user_id = project.project_manager_id
1319
 
            except Exception as exc:
1320
 
                print _("Failed to retrieve user info: %(exc)s") % exc
1321
 
                raise
1322
 
 
1323
 
        is_admin = self.manager.is_admin(user_id)
1324
 
        ctxt = context.RequestContext(user_id, project_id, is_admin)
1325
 
        if not is_admin and \
1326
 
           not self.manager.is_project_member(user_id, project_id):
1327
 
            msg = _("%(user_id)s must be an admin or a "
1328
 
                    "member of %(project_id)s")
1329
 
            LOG.warn(msg % locals())
1330
 
            raise ValueError(msg % locals())
1331
 
 
1332
 
        # Sanity check for storage string
1333
 
        storage_list = []
1334
 
        if storage is not None:
1335
 
            try:
1336
 
                storage_list = ast.literal_eval(storage)
1337
 
            except:
1338
 
                print _("Invalid string format %s") % storage
1339
 
                raise
1340
 
 
1341
 
            for node in storage_list:
1342
 
                if ('drive_name' not in node) or ('num_drives' not in node):
1343
 
                    print (_("Invalid string format for element %s. " \
1344
 
                            "Expecting keys 'drive_name' & 'num_drives'"),
1345
 
                            str(node))
1346
 
                    raise KeyError
1347
 
 
1348
 
        if instance_type_name == '':
1349
 
            instance_type_name = None
1350
 
        instance_type = instance_types.get_instance_type_by_name(
1351
 
                                instance_type_name)
1352
 
 
1353
 
        if image_name == '':
1354
 
            image_name = None
1355
 
 
1356
 
        if shared in [None, False, "--full_drives"]:
1357
 
            shared = False
1358
 
        elif shared in [True, "--shared"]:
1359
 
            shared = True
1360
 
        else:
1361
 
            raise ValueError(_('Shared parameter should be set either to "\
1362
 
                "--shared or --full_drives'))
1363
 
 
1364
 
        values = {
1365
 
            'display_name': name,
1366
 
            'display_description': description,
1367
 
            'vc_count': int(vc_count),
1368
 
            'instance_type': instance_type,
1369
 
            'image_name': image_name,
1370
 
            'availability_zone': az,
1371
 
            'storage': storage_list,
1372
 
            'shared': shared,
1373
 
            }
1374
 
 
1375
 
        result = self.vsa_api.create(ctxt, **values)
1376
 
        self._list(ctxt, [result])
1377
 
 
1378
 
    @args('--id', dest='vsa_id', metavar="<vsa_id>", help='VSA ID')
1379
 
    @args('--name', dest='name', metavar="<name>", help='VSA name')
1380
 
    @args('--description', dest='description', metavar="<description>",
1381
 
        help='VSA description')
1382
 
    @args('--vc', dest='vc_count', metavar="<number>", help='Number of VCs')
1383
 
    def update(self, vsa_id, name=None, description=None, vc_count=None):
1384
 
        """Updates name/description of vsa and number of VCs."""
1385
 
 
1386
 
        values = {}
1387
 
        if name is not None:
1388
 
            values['display_name'] = name
1389
 
        if description is not None:
1390
 
            values['display_description'] = description
1391
 
        if vc_count is not None:
1392
 
            values['vc_count'] = int(vc_count)
1393
 
 
1394
 
        vsa_id = ec2utils.ec2_id_to_id(vsa_id)
1395
 
        result = self.vsa_api.update(self.context, vsa_id=vsa_id, **values)
1396
 
        self._list(self.context, [result])
1397
 
 
1398
 
    @args('--id', dest='vsa_id', metavar="<vsa_id>", help='VSA ID')
1399
 
    def delete(self, vsa_id):
1400
 
        """Delete a VSA."""
1401
 
        vsa_id = ec2utils.ec2_id_to_id(vsa_id)
1402
 
        self.vsa_api.delete(self.context, vsa_id)
1403
 
 
1404
 
    @args('--id', dest='vsa_id', metavar="<vsa_id>",
1405
 
        help='VSA ID (optional)')
1406
 
    @args('--all', dest='all', action="store_true", default=False,
1407
 
        help='Show all available details')
1408
 
    @args('--drives', dest='drives', action="store_true",
1409
 
        help='Include drive-level details')
1410
 
    @args('--volumes', dest='volumes', action="store_true",
1411
 
        help='Include volume-level details')
1412
 
    @args('--instances', dest='instances', action="store_true",
1413
 
        help='Include instance-level details')
1414
 
    def list(self, vsa_id=None, all=False,
1415
 
             drives=False, volumes=False, instances=False):
1416
 
        """Describe all available VSAs (or particular one)."""
1417
 
 
1418
 
        vsas = []
1419
 
        if vsa_id is not None:
1420
 
            internal_id = ec2utils.ec2_id_to_id(vsa_id)
1421
 
            vsa = self.vsa_api.get(self.context, internal_id)
1422
 
            vsas.append(vsa)
1423
 
        else:
1424
 
            vsas = self.vsa_api.get_all(self.context)
1425
 
 
1426
 
        if all:
1427
 
            drives = volumes = instances = True
1428
 
 
1429
 
        self._list(self.context, vsas, drives, volumes, instances)
1430
 
 
1431
 
    def update_capabilities(self):
1432
 
        """Forces updates capabilities on all nova-volume nodes."""
1433
 
 
1434
 
        rpc.fanout_cast(context.get_admin_context(),
1435
 
                 FLAGS.volume_topic,
1436
 
                 {"method": "notification",
1437
 
                  "args": {"event": "startup"}})
1438
 
 
1439
 
 
1440
 
class VsaDriveTypeCommands(object):
1441
 
    """Methods for dealing with VSA drive types"""
1442
 
 
1443
 
    def __init__(self, *args, **kwargs):
1444
 
        super(VsaDriveTypeCommands, self).__init__(*args, **kwargs)
1445
 
        self.context = context.get_admin_context()
1446
 
        self._drive_type_template = '%s_%sGB_%sRPM'
1447
 
 
1448
 
    def _list(self, drives):
1449
 
        format_str = "%-5s %-30s %-10s %-10s %-10s %-20s %-10s %s"
1450
 
        if len(drives):
1451
 
            print format_str %\
1452
 
                   (_('ID'),
1453
 
                    _('name'),
1454
 
                    _('type'),
1455
 
                    _('size_gb'),
1456
 
                    _('rpm'),
1457
 
                    _('capabilities'),
1458
 
                    _('visible'),
1459
 
                    _('createTime'))
1460
 
 
1461
 
        for name, vol_type in drives.iteritems():
1462
 
            drive = vol_type.get('extra_specs')
1463
 
            print format_str %\
1464
 
                (str(vol_type['id']),
1465
 
                drive['drive_name'],
1466
 
                drive['drive_type'],
1467
 
                drive['drive_size'],
1468
 
                drive['drive_rpm'],
1469
 
                drive.get('capabilities', ''),
1470
 
                str(drive.get('visible', '')),
1471
 
                str(vol_type['created_at']))
1472
 
 
1473
 
    @args('--type', dest='type', metavar="<type>",
1474
 
        help='Drive type (SATA, SAS, SSD, etc.)')
1475
 
    @args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
1476
 
    @args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
1477
 
    @args('--capabilities', dest='capabilities', default=None,
1478
 
        metavar="<string>", help='Different capabilities')
1479
 
    @args('--hide', dest='hide', action="store_true", default=False,
1480
 
        help='Show or hide drive')
1481
 
    @args('--name', dest='name', metavar="<name>", help='Drive name')
1482
 
    def create(self, type, size_gb, rpm, capabilities=None,
1483
 
                     hide=False, name=None):
1484
 
        """Create drive type."""
1485
 
 
1486
 
        hide = True if hide in [True, "True", "--hide", "hide"] else False
1487
 
 
1488
 
        if name is None:
1489
 
            name = self._drive_type_template % (type, size_gb, rpm)
1490
 
 
1491
 
        extra_specs = {'type': 'vsa_drive',
1492
 
                       'drive_name': name,
1493
 
                       'drive_type': type,
1494
 
                       'drive_size': size_gb,
1495
 
                       'drive_rpm': rpm,
1496
 
                       'visible': True,
1497
 
                       }
1498
 
        if hide:
1499
 
            extra_specs['visible'] = False
1500
 
 
1501
 
        if capabilities is not None and capabilities != '':
1502
 
            extra_specs['capabilities'] = capabilities
1503
 
 
1504
 
        volume_types.create(self.context, name, extra_specs)
1505
 
        result = volume_types.get_volume_type_by_name(self.context, name)
1506
 
        self._list({name: result})
1507
 
 
1508
 
    @args('--name', dest='name', metavar="<name>", help='Drive name')
1509
 
    @args('--purge', action="store_true", dest='purge', default=False,
1510
 
            help='purge record from database')
1511
 
    def delete(self, name, purge):
1512
 
        """Marks instance types / flavors as deleted"""
1513
 
        try:
1514
 
            if purge:
1515
 
                volume_types.purge(self.context, name)
1516
 
                verb = "purged"
1517
 
            else:
1518
 
                volume_types.destroy(self.context, name)
1519
 
                verb = "deleted"
1520
 
        except exception.ApiError:
1521
 
            print "Valid volume type name is required"
1522
 
            sys.exit(1)
1523
 
        except exception.DBError, e:
1524
 
            print "DB Error: %s" % e
1525
 
            sys.exit(2)
1526
 
        except:
1527
 
            sys.exit(3)
1528
 
        else:
1529
 
            print "%s %s" % (name, verb)
1530
 
 
1531
 
    @args('--all', dest='all', action="store_true", default=False,
1532
 
        help='Show all drives (including invisible)')
1533
 
    @args('--name', dest='name', metavar="<name>",
1534
 
        help='Show only specified drive')
1535
 
    def list(self, all=False, name=None):
1536
 
        """Describe all available VSA drive types (or particular one)."""
1537
 
 
1538
 
        all = False if all in ["--all", False, "False"] else True
1539
 
 
1540
 
        search_opts = {'extra_specs': {'type': 'vsa_drive'}}
1541
 
        if name is not None:
1542
 
            search_opts['extra_specs']['name'] = name
1543
 
 
1544
 
        if all == False:
1545
 
            search_opts['extra_specs']['visible'] = '1'
1546
 
 
1547
 
        drives = volume_types.get_all_types(self.context,
1548
 
                                            search_opts=search_opts)
1549
 
        self._list(drives)
1550
 
 
1551
 
    @args('--name', dest='name', metavar="<name>", help='Drive name')
1552
 
    @args('--type', dest='type', metavar="<type>",
1553
 
        help='Drive type (SATA, SAS, SSD, etc.)')
1554
 
    @args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
1555
 
    @args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
1556
 
    @args('--capabilities', dest='capabilities', default=None,
1557
 
        metavar="<string>", help='Different capabilities')
1558
 
    @args('--visible', dest='visible',
1559
 
        metavar="<show|hide>", help='Show or hide drive')
1560
 
    def update(self, name, type=None, size_gb=None, rpm=None,
1561
 
                     capabilities=None, visible=None):
1562
 
        """Update drive type."""
1563
 
 
1564
 
        volume_type = volume_types.get_volume_type_by_name(self.context, name)
1565
 
 
1566
 
        extra_specs = {'type': 'vsa_drive'}
1567
 
 
1568
 
        if type:
1569
 
            extra_specs['drive_type'] = type
1570
 
 
1571
 
        if size_gb:
1572
 
            extra_specs['drive_size'] = size_gb
1573
 
 
1574
 
        if rpm:
1575
 
            extra_specs['drive_rpm'] = rpm
1576
 
 
1577
 
        if capabilities:
1578
 
            extra_specs['capabilities'] = capabilities
1579
 
 
1580
 
        if visible is not None:
1581
 
            if visible in ["show", True, "True"]:
1582
 
                extra_specs['visible'] = True
1583
 
            elif visible in ["hide", False, "False"]:
1584
 
                extra_specs['visible'] = False
1585
 
            else:
1586
 
                raise ValueError(_('visible parameter should be set to '\
1587
 
                                   'show or hide'))
1588
 
 
1589
 
        db.api.volume_type_extra_specs_update_or_create(self.context,
1590
 
                    volume_type['id'],
1591
 
                    extra_specs)
1592
 
        result = volume_types.get_volume_type_by_name(self.context, name)
1593
 
        self._list({name: result})
1594
 
 
1595
 
 
1596
 
class VolumeCommands(object):
1597
 
    """Methods for dealing with a cloud in an odd state"""
1598
 
 
1599
 
    @args('--volume', dest='volume_id', metavar='<volume id>',
1600
 
            help='Volume ID')
1601
 
    def delete(self, volume_id):
1602
 
        """Delete a volume, bypassing the check that it
1603
 
        must be available."""
1604
 
        ctxt = context.get_admin_context()
1605
 
        volume = db.volume_get(ctxt, param2id(volume_id))
1606
 
        host = volume['host']
1607
 
 
1608
 
        if not host:
1609
 
            print "Volume not yet assigned to host."
1610
 
            print "Deleting volume from database and skipping rpc."
1611
 
            db.volume_destroy(ctxt, param2id(volume_id))
1612
 
            return
1613
 
 
1614
 
        if volume['status'] == 'in-use':
1615
 
            print "Volume is in-use."
1616
 
            print "Detach volume from instance and then try again."
1617
 
            return
1618
 
 
1619
 
        rpc.cast(ctxt,
1620
 
                 db.queue_get_for(ctxt, FLAGS.volume_topic, host),
1621
 
                 {"method": "delete_volume",
1622
 
                  "args": {"volume_id": volume['id']}})
1623
 
 
1624
 
    @args('--volume', dest='volume_id', metavar='<volume id>',
1625
 
            help='Volume ID')
1626
 
    def reattach(self, volume_id):
1627
 
        """Re-attach a volume that has previously been attached
1628
 
        to an instance.  Typically called after a compute host
1629
 
        has been rebooted."""
1630
 
        ctxt = context.get_admin_context()
1631
 
        volume = db.volume_get(ctxt, param2id(volume_id))
1632
 
        if not volume['instance_id']:
1633
 
            print "volume is not attached to an instance"
1634
 
            return
1635
 
        instance = db.instance_get(ctxt, volume['instance_id'])
1636
 
        host = instance['host']
1637
 
        rpc.cast(ctxt,
1638
 
                 db.queue_get_for(ctxt, FLAGS.compute_topic, host),
1639
 
                 {"method": "attach_volume",
1640
 
                  "args": {"instance_id": instance['id'],
1641
 
                           "volume_id": volume['id'],
1642
 
                           "mountpoint": volume['mountpoint']}})
1643
 
 
1644
 
 
1645
 
class InstanceTypeCommands(object):
1646
 
    """Class for managing instance types / flavors."""
1647
 
 
1648
 
    def _print_instance_types(self, name, val):
1649
 
        deleted = ('', ', inactive')[val["deleted"] == 1]
1650
 
        print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, "
1651
 
            "Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % (
1652
 
            name, val["memory_mb"], val["vcpus"], val["local_gb"],
1653
 
            val["flavorid"], val["swap"], val["rxtx_quota"],
1654
 
            val["rxtx_cap"], deleted)
1655
 
 
1656
 
    @args('--name', dest='name', metavar='<name>',
1657
 
            help='Name of instance type/flavor')
1658
 
    @args('--memory', dest='memory', metavar='<memory size>',
1659
 
            help='Memory size')
1660
 
    @args('--cpu', dest='vcpus', metavar='<num cores>', help='Number cpus')
1661
 
    @args('--local_gb', dest='local_gb', metavar='<local_gb>',
1662
 
            help='local_gb')
1663
 
    @args('--flavor', dest='flavorid', metavar='<flavor  id>',
1664
 
            help='Flavor ID')
1665
 
    @args('--swap', dest='swap', metavar='<swap>', help='Swap')
1666
 
    @args('--rxtx_quota', dest='rxtx_quota', metavar='<rxtx_quota>',
1667
 
            help='rxtx_quota')
1668
 
    @args('--rxtx_cap', dest='rxtx_cap', metavar='<rxtx_cap>',
1669
 
            help='rxtx_cap')
1670
 
    def create(self, name, memory, vcpus, local_gb, flavorid,
1671
 
               swap=0, rxtx_quota=0, rxtx_cap=0):
1672
 
        """Creates instance types / flavors"""
1673
 
        try:
1674
 
            instance_types.create(name, memory, vcpus, local_gb,
1675
 
                                  flavorid, swap, rxtx_quota, rxtx_cap)
1676
 
        except exception.InvalidInput, e:
1677
 
            print "Must supply valid parameters to create instance_type"
1678
 
            print e
1679
 
            sys.exit(1)
1680
 
        except exception.ApiError, e:
1681
 
            print "\n\n"
1682
 
            print "\n%s" % e
1683
 
            print "Please ensure instance_type name and flavorid are unique."
1684
 
            print "To complete remove a instance_type, use the --purge flag:"
1685
 
            print "\n     # nova-manage instance_type delete <name> --purge\n"
1686
 
            print "Currently defined instance_type names and flavorids:"
1687
 
            self.list("--all")
1688
 
            sys.exit(2)
1689
 
        except:
1690
 
            print "Unknown error"
1691
 
            sys.exit(3)
1692
 
        else:
1693
 
            print "%s created" % name
1694
 
 
1695
 
    @args('--name', dest='name', metavar='<name>',
1696
 
            help='Name of instance type/flavor')
1697
 
    @args('--purge', action="store_true", dest='purge', default=False,
1698
 
            help='purge record from database')
1699
 
    def delete(self, name, purge):
1700
 
        """Marks instance types / flavors as deleted"""
1701
 
        try:
1702
 
            if purge:
1703
 
                instance_types.purge(name)
1704
 
                verb = "purged"
1705
 
            else:
1706
 
                instance_types.destroy(name)
1707
 
                verb = "deleted"
1708
 
        except exception.ApiError:
1709
 
            print "Valid instance type name is required"
1710
 
            sys.exit(1)
1711
 
        except exception.DBError, e:
1712
 
            print "DB Error: %s" % e
1713
 
            sys.exit(2)
1714
 
        except:
1715
 
            sys.exit(3)
1716
 
        else:
1717
 
            print "%s %s" % (name, verb)
1718
 
 
1719
 
    @args('--name', dest='name', metavar='<name>',
1720
 
            help='Name of instance type/flavor')
1721
 
    def list(self, name=None):
1722
 
        """Lists all active or specific instance types / flavors"""
1723
 
        try:
1724
 
            if name is None:
1725
 
                inst_types = instance_types.get_all_types()
1726
 
            elif name == "--all":
1727
 
                inst_types = instance_types.get_all_types(True)
1728
 
            else:
1729
 
                inst_types = instance_types.get_instance_type_by_name(name)
1730
 
        except exception.DBError, e:
1731
 
            _db_error(e)
1732
 
        if isinstance(inst_types.values()[0], dict):
1733
 
            for k, v in inst_types.iteritems():
1734
 
                self._print_instance_types(k, v)
1735
 
        else:
1736
 
            self._print_instance_types(name, inst_types)
1737
 
 
1738
 
 
1739
 
class ImageCommands(object):
1740
 
    """Methods for dealing with a cloud in an odd state"""
1741
 
 
1742
 
    def __init__(self, *args, **kwargs):
1743
 
        self.image_service = image.get_default_image_service()
1744
 
 
1745
 
    def _register(self, container_format, disk_format,
1746
 
                  path, owner, name=None, is_public='T',
1747
 
                  architecture='x86_64', kernel_id=None, ramdisk_id=None):
1748
 
        meta = {'is_public': (is_public == 'T'),
1749
 
                'name': name,
1750
 
                'container_format': container_format,
1751
 
                'disk_format': disk_format,
1752
 
                'properties': {'image_state': 'available',
1753
 
                               'project_id': owner,
1754
 
                               'architecture': architecture,
1755
 
                               'image_location': 'local'}}
1756
 
        if kernel_id:
1757
 
            meta['properties']['kernel_id'] = int(kernel_id)
1758
 
        if ramdisk_id:
1759
 
            meta['properties']['ramdisk_id'] = int(ramdisk_id)
1760
 
        elevated = context.get_admin_context()
1761
 
        try:
1762
 
            with open(path) as ifile:
1763
 
                image = self.image_service.create(elevated, meta, ifile)
1764
 
            new = image['id']
1765
 
            print _("Image registered to %(new)s (%(new)08x).") % locals()
1766
 
            return new
1767
 
        except Exception as exc:
1768
 
            print _("Failed to register %(path)s: %(exc)s") % locals()
1769
 
 
1770
 
    @args('--image', dest='image', metavar='<image>', help='Image')
1771
 
    @args('--kernel', dest='kernel', metavar='<kernel>', help='Kernel')
1772
 
    @args('--ram', dest='ramdisk', metavar='<ramdisk>', help='RAM disk')
1773
 
    @args('--owner', dest='owner', metavar='<owner>', help='Image owner')
1774
 
    @args('--name', dest='name', metavar='<name>', help='Image name')
1775
 
    @args('--public', dest='is_public', metavar="<'T'|'F'>",
1776
 
            help='Image public or not')
1777
 
    @args('--arch', dest='architecture', metavar='<arch>',
1778
 
            help='Architecture')
1779
 
    def all_register(self, image, kernel, ramdisk, owner, name=None,
1780
 
                 is_public='T', architecture='x86_64'):
1781
 
        """Uploads an image, kernel, and ramdisk into the image_service"""
1782
 
        kernel_id = self.kernel_register(kernel, owner, None,
1783
 
                                   is_public, architecture)
1784
 
        ramdisk_id = self.ramdisk_register(ramdisk, owner, None,
1785
 
                                    is_public, architecture)
1786
 
        self.image_register(image, owner, name, is_public,
1787
 
                            architecture, 'ami', 'ami',
1788
 
                            kernel_id, ramdisk_id)
1789
 
 
1790
 
    @args('--path', dest='path', metavar='<path>', help='Image path')
1791
 
    @args('--owner', dest='owner', metavar='<owner>', help='Image owner')
1792
 
    @args('--name', dest='name', metavar='<name>', help='Image name')
1793
 
    @args('--public', dest='is_public', metavar="<'T'|'F'>",
1794
 
            help='Image public or not')
1795
 
    @args('--arch', dest='architecture', metavar='<arch>',
1796
 
            help='Architecture')
1797
 
    @args('--cont_format', dest='container_format',
1798
 
            metavar='<container format>',
1799
 
            help='Container format(default bare)')
1800
 
    @args('--disk_format', dest='disk_format', metavar='<disk format>',
1801
 
            help='Disk format(default: raw)')
1802
 
    @args('--kernel', dest='kernel_id', metavar='<kernel>', help='Kernel')
1803
 
    @args('--ram', dest='ramdisk_id', metavar='<ramdisk>', help='RAM disk')
1804
 
    def image_register(self, path, owner, name=None, is_public='T',
1805
 
                       architecture='x86_64', container_format='bare',
1806
 
                       disk_format='raw', kernel_id=None, ramdisk_id=None):
1807
 
        """Uploads an image into the image_service"""
1808
 
        return self._register(container_format, disk_format, path,
1809
 
                              owner, name, is_public, architecture,
1810
 
                              kernel_id, ramdisk_id)
1811
 
 
1812
 
    @args('--path', dest='path', metavar='<path>', help='Image path')
1813
 
    @args('--owner', dest='owner', metavar='<owner>', help='Image owner')
1814
 
    @args('--name', dest='name', metavar='<name>', help='Image name')
1815
 
    @args('--public', dest='is_public', metavar="<'T'|'F'>",
1816
 
            help='Image public or not')
1817
 
    @args('--arch', dest='architecture', metavar='<arch>',
1818
 
            help='Architecture')
1819
 
    def kernel_register(self, path, owner, name=None, is_public='T',
1820
 
               architecture='x86_64'):
1821
 
        """Uploads a kernel into the image_service"""
1822
 
        return self._register('aki', 'aki', path, owner, name,
1823
 
                              is_public, architecture)
1824
 
 
1825
 
    @args('--path', dest='path', metavar='<path>', help='Image path')
1826
 
    @args('--owner', dest='owner', metavar='<owner>', help='Image owner')
1827
 
    @args('--name', dest='name', metavar='<name>', help='Image name')
1828
 
    @args('--public', dest='is_public', metavar="<'T'|'F'>",
1829
 
            help='Image public or not')
1830
 
    @args('--arch', dest='architecture', metavar='<arch>',
1831
 
            help='Architecture')
1832
 
    def ramdisk_register(self, path, owner, name=None, is_public='T',
1833
 
                architecture='x86_64'):
1834
 
        """Uploads a ramdisk into the image_service"""
1835
 
        return self._register('ari', 'ari', path, owner, name,
1836
 
                              is_public, architecture)
1837
 
 
1838
 
    def _lookup(self, old_image_id):
1839
 
        elevated = context.get_admin_context()
1840
 
        try:
1841
 
            internal_id = ec2utils.ec2_id_to_id(old_image_id)
1842
 
            image = self.image_service.show(elevated, internal_id)
1843
 
        except (exception.InvalidEc2Id, exception.ImageNotFound):
1844
 
            image = self.image_service.show_by_name(elevated, old_image_id)
1845
 
        return image['id']
1846
 
 
1847
 
    def _old_to_new(self, old):
1848
 
        mapping = {'machine': 'ami',
1849
 
                   'kernel': 'aki',
1850
 
                   'ramdisk': 'ari'}
1851
 
        container_format = mapping[old['type']]
1852
 
        disk_format = container_format
1853
 
        if container_format == 'ami' and not old.get('kernelId'):
1854
 
            container_format = 'bare'
1855
 
            disk_format = 'raw'
1856
 
        new = {'disk_format': disk_format,
1857
 
               'container_format': container_format,
1858
 
               'is_public': old['isPublic'],
1859
 
               'name': old['imageId'],
1860
 
               'properties': {'image_state': old['imageState'],
1861
 
                              'project_id': old['imageOwnerId'],
1862
 
                              'architecture': old['architecture'],
1863
 
                              'image_location': old['imageLocation']}}
1864
 
        if old.get('kernelId'):
1865
 
            new['properties']['kernel_id'] = self._lookup(old['kernelId'])
1866
 
        if old.get('ramdiskId'):
1867
 
            new['properties']['ramdisk_id'] = self._lookup(old['ramdiskId'])
1868
 
        return new
1869
 
 
1870
 
    def _convert_images(self, images):
1871
 
        elevated = context.get_admin_context()
1872
 
        for image_path, image_metadata in images.iteritems():
1873
 
            meta = self._old_to_new(image_metadata)
1874
 
            old = meta['name']
1875
 
            try:
1876
 
                with open(image_path) as ifile:
1877
 
                    image = self.image_service.create(elevated, meta, ifile)
1878
 
                new = image['id']
1879
 
                print _("Image %(old)s converted to " \
1880
 
                        "%(new)s (%(new)08x).") % locals()
1881
 
            except Exception as exc:
1882
 
                print _("Failed to convert %(old)s: %(exc)s") % locals()
1883
 
 
1884
 
    @args('--dir', dest='directory', metavar='<path>',
1885
 
            help='Images directory')
1886
 
    def convert(self, directory):
1887
 
        """Uploads old objectstore images in directory to new service"""
1888
 
        machine_images = {}
1889
 
        other_images = {}
1890
 
        directory = os.path.abspath(directory)
1891
 
        for fn in glob.glob("%s/*/info.json" % directory):
1892
 
            try:
1893
 
                image_path = os.path.join(fn.rpartition('/')[0], 'image')
1894
 
                with open(fn) as metadata_file:
1895
 
                    image_metadata = json.load(metadata_file)
1896
 
                if image_metadata['type'] == 'machine':
1897
 
                    machine_images[image_path] = image_metadata
1898
 
                else:
1899
 
                    other_images[image_path] = image_metadata
1900
 
            except Exception:
1901
 
                print _("Failed to load %(fn)s.") % locals()
1902
 
        # NOTE(vish): do kernels and ramdisks first so images
1903
 
        self._convert_images(other_images)
1904
 
        self._convert_images(machine_images)
1905
 
 
1906
 
 
1907
 
class AgentBuildCommands(object):
1908
 
    """Class for managing agent builds."""
1909
 
 
1910
 
    def create(self, os, architecture, version, url, md5hash,
1911
 
                hypervisor='xen'):
1912
 
        """Creates a new agent build."""
1913
 
        ctxt = context.get_admin_context()
1914
 
        agent_build = db.agent_build_create(ctxt,
1915
 
                                            {'hypervisor': hypervisor,
1916
 
                                             'os': os,
1917
 
                                             'architecture': architecture,
1918
 
                                             'version': version,
1919
 
                                             'url': url,
1920
 
                                             'md5hash': md5hash})
1921
 
 
1922
 
    def delete(self, os, architecture, hypervisor='xen'):
1923
 
        """Deletes an existing agent build."""
1924
 
        ctxt = context.get_admin_context()
1925
 
        agent_build_ref = db.agent_build_get_by_triple(ctxt,
1926
 
                                  hypervisor, os, architecture)
1927
 
        db.agent_build_destroy(ctxt, agent_build_ref['id'])
1928
 
 
1929
 
    def list(self, hypervisor=None):
1930
 
        """Lists all agent builds.
1931
 
        arguments: <none>"""
1932
 
        fmt = "%-10s  %-8s  %12s  %s"
1933
 
        ctxt = context.get_admin_context()
1934
 
        by_hypervisor = {}
1935
 
        for agent_build in db.agent_build_get_all(ctxt):
1936
 
            buildlist = by_hypervisor.get(agent_build.hypervisor)
1937
 
            if not buildlist:
1938
 
                buildlist = by_hypervisor[agent_build.hypervisor] = []
1939
 
 
1940
 
            buildlist.append(agent_build)
1941
 
 
1942
 
        for key, buildlist in by_hypervisor.iteritems():
1943
 
            if hypervisor and key != hypervisor:
1944
 
                continue
1945
 
 
1946
 
            print "Hypervisor: %s" % key
1947
 
            print fmt % ('-' * 10, '-' * 8, '-' * 12, '-' * 32)
1948
 
            for agent_build in buildlist:
1949
 
                print fmt % (agent_build.os, agent_build.architecture,
1950
 
                             agent_build.version, agent_build.md5hash)
1951
 
                print '    %s' % agent_build.url
1952
 
 
1953
 
            print
1954
 
 
1955
 
    def modify(self, os, architecture, version, url, md5hash,
1956
 
               hypervisor='xen'):
1957
 
        """Update an existing agent build."""
1958
 
        ctxt = context.get_admin_context()
1959
 
        agent_build_ref = db.agent_build_get_by_triple(ctxt,
1960
 
                                  hypervisor, os, architecture)
1961
 
        db.agent_build_update(ctxt, agent_build_ref['id'],
1962
 
                              {'version': version,
1963
 
                               'url': url,
1964
 
                               'md5hash': md5hash})
1965
 
 
1966
 
 
1967
 
class ConfigCommands(object):
1968
 
    """Class for exposing the flags defined by flag_file(s)."""
1969
 
 
1970
 
    def __init__(self):
1971
 
        pass
1972
 
 
1973
 
    def list(self):
1974
 
        print FLAGS.FlagsIntoString()
 
433
                                    fixed_range, int(num_networks),
 
434
                                    int(network_size), int(vlan_start),
 
435
                                    int(vpn_start),fixed_range_v6)
 
436
                                    
1975
437
 
1976
438
 
1977
439
CATEGORIES = [
1978
 
    ('account', AccountCommands),
1979
 
    ('agent', AgentBuildCommands),
1980
 
    ('config', ConfigCommands),
1981
 
    ('db', DbCommands),
1982
 
    ('drive', VsaDriveTypeCommands),
1983
 
    ('fixed', FixedIpCommands),
1984
 
    ('flavor', InstanceTypeCommands),
1985
 
    ('floating', FloatingIpCommands),
1986
 
    ('host', HostCommands),
1987
 
    ('instance_type', InstanceTypeCommands),
1988
 
    ('image', ImageCommands),
1989
 
    ('network', NetworkCommands),
 
440
    ('user', UserCommands),
1990
441
    ('project', ProjectCommands),
1991
442
    ('role', RoleCommands),
1992
 
    ('service', ServiceCommands),
1993
443
    ('shell', ShellCommands),
1994
 
    ('user', UserCommands),
1995
 
    ('version', VersionCommands),
1996
 
    ('vm', VmCommands),
1997
 
    ('volume', VolumeCommands),
1998
444
    ('vpn', VpnCommands),
1999
 
    ('vsa', VsaCommands)]
 
445
    ('floating', FloatingIpCommands),
 
446
    ('network', NetworkCommands)]
2000
447
 
2001
448
 
2002
449
def lazy_match(name, key_value_tuples):
2034
481
    """Parse options and call the appropriate class/method."""
2035
482
    utils.default_flagfile()
2036
483
    argv = FLAGS(sys.argv)
2037
 
    logging.setup()
 
484
 
 
485
    if FLAGS.verbose:
 
486
        logging.getLogger().setLevel(logging.DEBUG)
2038
487
 
2039
488
    script_name = argv.pop(0)
2040
489
    if len(argv) < 1:
2041
 
        print _("\nOpenStack Nova version: %s (%s)\n") %\
2042
 
                (version.version_string(), version.version_string_with_vcs())
2043
490
        print script_name + " category action [<args>]"
2044
 
        print _("Available categories:")
2045
 
        for k, _v in CATEGORIES:
 
491
        print "Available categories:"
 
492
        for k, _ in CATEGORIES:
2046
493
            print "\t%s" % k
2047
494
        sys.exit(2)
2048
495
    category = argv.pop(0)
2052
499
    command_object = fn()
2053
500
    actions = methods_of(command_object)
2054
501
    if len(argv) < 1:
2055
 
        if hasattr(command_object, '__call__'):
2056
 
            action = ''
2057
 
            fn = command_object.__call__
2058
 
        else:
2059
 
            print script_name + " category action [<args>]"
2060
 
            print _("Available actions for %s category:") % category
2061
 
            for k, _v in actions:
2062
 
                print "\t%s" % k
2063
 
            sys.exit(2)
2064
 
    else:
2065
 
        action = argv.pop(0)
2066
 
        matches = lazy_match(action, actions)
2067
 
        action, fn = matches[0]
2068
 
 
2069
 
    # For not decorated methods
2070
 
    options = getattr(fn, 'options', [])
2071
 
 
2072
 
    usage = "%%prog %s %s <args> [options]" % (category, action)
2073
 
    parser = OptionParser(usage=usage)
2074
 
    for ar, kw in options:
2075
 
        parser.add_option(*ar, **kw)
2076
 
    (opts, fn_args) = parser.parse_args(argv)
2077
 
    fn_kwargs = vars(opts)
2078
 
 
2079
 
    for k, v in fn_kwargs.items():
2080
 
        if v is None:
2081
 
            del fn_kwargs[k]
2082
 
 
 
502
        print script_name + " category action [<args>]"
 
503
        print "Available actions for %s category:" % category
 
504
        for k, _v in actions:
 
505
            print "\t%s" % k
 
506
        sys.exit(2)
 
507
    action = argv.pop(0)
 
508
    matches = lazy_match(action, actions)
 
509
    action, fn = matches[0]
2083
510
    # call the action with the remaining arguments
2084
511
    try:
2085
 
        fn(*fn_args, **fn_kwargs)
 
512
        fn(*argv)
2086
513
        sys.exit(0)
2087
514
    except TypeError:
2088
 
        print _("Possible wrong number of arguments supplied")
2089
 
        print fn.__doc__
2090
 
        parser.print_help()
2091
 
        raise
2092
 
    except Exception:
2093
 
        print _("Command failed, please check log for more info")
 
515
        print "Possible wrong number of arguments supplied"
 
516
        print "%s %s: %s" % (category, action, fn.__doc__)
2094
517
        raise
2095
518
 
2096
519
if __name__ == '__main__':