~cloudbuilders/nova/os-keypair-integration

« back to all changes in this revision

Viewing changes to bin/nova-manage

  • Committer: Vishvananda Ishaya
  • Date: 2011-08-31 18:51:19 UTC
  • mfrom: (1455.1.56 nova)
  • Revision ID: vishvananda@gmail.com-20110831185119-04pppkh6iv7v6p6i
merged trunk

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
56
57
import gettext
57
58
import glob
58
59
import json
85
86
from nova import rpc
86
87
from nova import utils
87
88
from nova import version
 
89
from nova import vsa
88
90
from nova.api.ec2 import ec2utils
89
91
from nova.auth import manager
90
92
from nova.cloudpipe import pipelib
91
93
from nova.compute import instance_types
92
94
from nova.db import migration
 
95
from nova.volume import volume_types
93
96
 
94
97
FLAGS = flags.FLAGS
95
98
flags.DECLARE('fixed_range', 'nova.network.manager')
1097
1100
        self.list()
1098
1101
 
1099
1102
 
 
1103
class VsaCommands(object):
 
1104
    """Methods for dealing with VSAs"""
 
1105
 
 
1106
    def __init__(self, *args, **kwargs):
 
1107
        self.manager = manager.AuthManager()
 
1108
        self.vsa_api = vsa.API()
 
1109
        self.context = context.get_admin_context()
 
1110
 
 
1111
        self._format_str_vsa = "%(id)-5s %(vsa_id)-15s %(name)-25s "\
 
1112
                    "%(type)-10s %(vcs)-6s %(drives)-9s %(stat)-10s "\
 
1113
                    "%(az)-10s %(time)-10s"
 
1114
        self._format_str_volume = "\t%(id)-4s %(name)-15s %(size)-5s "\
 
1115
                    "%(stat)-10s %(att)-20s %(time)s"
 
1116
        self._format_str_drive = "\t%(id)-4s %(name)-15s %(size)-5s "\
 
1117
                    "%(stat)-10s %(host)-20s %(type)-4s %(tname)-10s %(time)s"
 
1118
        self._format_str_instance = "\t%(id)-4s %(name)-10s %(dname)-20s "\
 
1119
                    "%(image)-12s %(type)-10s %(fl_ip)-15s %(fx_ip)-15s "\
 
1120
                    "%(stat)-10s %(host)-15s %(time)s"
 
1121
 
 
1122
    def _print_vsa_header(self):
 
1123
        print self._format_str_vsa %\
 
1124
               dict(id=_('ID'),
 
1125
                    vsa_id=_('vsa_id'),
 
1126
                    name=_('displayName'),
 
1127
                    type=_('vc_type'),
 
1128
                    vcs=_('vc_cnt'),
 
1129
                    drives=_('drive_cnt'),
 
1130
                    stat=_('status'),
 
1131
                    az=_('AZ'),
 
1132
                    time=_('createTime'))
 
1133
 
 
1134
    def _print_vsa(self, vsa):
 
1135
        print self._format_str_vsa %\
 
1136
                dict(id=vsa['id'],
 
1137
                     vsa_id=vsa['name'],
 
1138
                     name=vsa['display_name'],
 
1139
                     type=vsa['vsa_instance_type'].get('name', None),
 
1140
                     vcs=vsa['vc_count'],
 
1141
                     drives=vsa['vol_count'],
 
1142
                     stat=vsa['status'],
 
1143
                     az=vsa['availability_zone'],
 
1144
                     time=str(vsa['created_at']))
 
1145
 
 
1146
    def _print_volume_header(self):
 
1147
        print _('      === Volumes ===')
 
1148
        print self._format_str_volume %\
 
1149
                dict(id=_('ID'),
 
1150
                     name=_('name'),
 
1151
                     size=_('size'),
 
1152
                     stat=_('status'),
 
1153
                     att=_('attachment'),
 
1154
                     time=_('createTime'))
 
1155
 
 
1156
    def _print_volume(self, vol):
 
1157
        print self._format_str_volume %\
 
1158
                dict(id=vol['id'],
 
1159
                     name=vol['display_name'] or vol['name'],
 
1160
                     size=vol['size'],
 
1161
                     stat=vol['status'],
 
1162
                     att=vol['attach_status'],
 
1163
                     time=str(vol['created_at']))
 
1164
 
 
1165
    def _print_drive_header(self):
 
1166
        print _('      === Drives ===')
 
1167
        print self._format_str_drive %\
 
1168
                dict(id=_('ID'),
 
1169
                    name=_('name'),
 
1170
                    size=_('size'),
 
1171
                    stat=_('status'),
 
1172
                    host=_('host'),
 
1173
                    type=_('type'),
 
1174
                    tname=_('typeName'),
 
1175
                    time=_('createTime'))
 
1176
 
 
1177
    def _print_drive(self, drive):
 
1178
        if drive['volume_type_id'] is not None and drive.get('volume_type'):
 
1179
            drive_type_name = drive['volume_type'].get('name')
 
1180
        else:
 
1181
            drive_type_name = ''
 
1182
 
 
1183
        print self._format_str_drive %\
 
1184
                dict(id=drive['id'],
 
1185
                    name=drive['display_name'],
 
1186
                    size=drive['size'],
 
1187
                    stat=drive['status'],
 
1188
                    host=drive['host'],
 
1189
                    type=drive['volume_type_id'],
 
1190
                    tname=drive_type_name,
 
1191
                    time=str(drive['created_at']))
 
1192
 
 
1193
    def _print_instance_header(self):
 
1194
        print _('      === Instances ===')
 
1195
        print self._format_str_instance %\
 
1196
                dict(id=_('ID'),
 
1197
                    name=_('name'),
 
1198
                    dname=_('disp_name'),
 
1199
                    image=_('image'),
 
1200
                    type=_('type'),
 
1201
                    fl_ip=_('floating_IP'),
 
1202
                    fx_ip=_('fixed_IP'),
 
1203
                    stat=_('status'),
 
1204
                    host=_('host'),
 
1205
                    time=_('createTime'))
 
1206
 
 
1207
    def _print_instance(self, vc):
 
1208
 
 
1209
        fixed_addr = None
 
1210
        floating_addr = None
 
1211
        if vc['fixed_ips']:
 
1212
            fixed = vc['fixed_ips'][0]
 
1213
            fixed_addr = fixed['address']
 
1214
            if fixed['floating_ips']:
 
1215
                floating_addr = fixed['floating_ips'][0]['address']
 
1216
        floating_addr = floating_addr or fixed_addr
 
1217
 
 
1218
        print self._format_str_instance %\
 
1219
                dict(id=vc['id'],
 
1220
                    name=ec2utils.id_to_ec2_id(vc['id']),
 
1221
                    dname=vc['display_name'],
 
1222
                    image=('ami-%08x' % int(vc['image_ref'])),
 
1223
                    type=vc['instance_type']['name'],
 
1224
                    fl_ip=floating_addr,
 
1225
                    fx_ip=fixed_addr,
 
1226
                    stat=vc['state_description'],
 
1227
                    host=vc['host'],
 
1228
                    time=str(vc['created_at']))
 
1229
 
 
1230
    def _list(self, context, vsas, print_drives=False,
 
1231
                    print_volumes=False, print_instances=False):
 
1232
        if vsas:
 
1233
            self._print_vsa_header()
 
1234
 
 
1235
        for vsa in vsas:
 
1236
            self._print_vsa(vsa)
 
1237
            vsa_id = vsa.get('id')
 
1238
 
 
1239
            if print_instances:
 
1240
                instances = self.vsa_api.get_all_vsa_instances(context, vsa_id)
 
1241
                if instances:
 
1242
                    print
 
1243
                    self._print_instance_header()
 
1244
                    for instance in instances:
 
1245
                        self._print_instance(instance)
 
1246
                    print
 
1247
 
 
1248
            if print_drives:
 
1249
                drives = self.vsa_api.get_all_vsa_drives(context, vsa_id)
 
1250
                if drives:
 
1251
                    self._print_drive_header()
 
1252
                    for drive in drives:
 
1253
                        self._print_drive(drive)
 
1254
                    print
 
1255
 
 
1256
            if print_volumes:
 
1257
                volumes = self.vsa_api.get_all_vsa_volumes(context, vsa_id)
 
1258
                if volumes:
 
1259
                    self._print_volume_header()
 
1260
                    for volume in volumes:
 
1261
                        self._print_volume(volume)
 
1262
                    print
 
1263
 
 
1264
    @args('--storage', dest='storage',
 
1265
        metavar="[{'drive_name': 'type', 'num_drives': N, 'size': M},..]",
 
1266
        help='Initial storage allocation for VSA')
 
1267
    @args('--name', dest='name', metavar="<name>", help='VSA name')
 
1268
    @args('--description', dest='description', metavar="<description>",
 
1269
        help='VSA description')
 
1270
    @args('--vc', dest='vc_count', metavar="<number>", help='Number of VCs')
 
1271
    @args('--instance_type', dest='instance_type_name', metavar="<name>",
 
1272
        help='Instance type name')
 
1273
    @args('--image', dest='image_name', metavar="<name>", help='Image name')
 
1274
    @args('--shared', dest='shared', action="store_true", default=False,
 
1275
        help='Use shared drives')
 
1276
    @args('--az', dest='az', metavar="<zone:host>", help='Availability zone')
 
1277
    @args('--user', dest="user_id", metavar='<User name>',
 
1278
            help='User name')
 
1279
    @args('--project', dest="project_id", metavar='<Project name>',
 
1280
            help='Project name')
 
1281
    def create(self, storage='[]', name=None, description=None, vc_count=1,
 
1282
                     instance_type_name=None, image_name=None, shared=None,
 
1283
                     az=None, user_id=None, project_id=None):
 
1284
        """Create a VSA."""
 
1285
 
 
1286
        if project_id is None:
 
1287
            try:
 
1288
                project_id = os.getenv("EC2_ACCESS_KEY").split(':')[1]
 
1289
            except Exception as exc:
 
1290
                print _("Failed to retrieve project id: %(exc)s") % exc
 
1291
                raise
 
1292
 
 
1293
        if user_id is None:
 
1294
            try:
 
1295
                project = self.manager.get_project(project_id)
 
1296
                user_id = project.project_manager_id
 
1297
            except Exception as exc:
 
1298
                print _("Failed to retrieve user info: %(exc)s") % exc
 
1299
                raise
 
1300
 
 
1301
        is_admin = self.manager.is_admin(user_id)
 
1302
        ctxt = context.RequestContext(user_id, project_id, is_admin)
 
1303
        if not is_admin and \
 
1304
           not self.manager.is_project_member(user_id, project_id):
 
1305
            msg = _("%(user_id)s must be an admin or a "
 
1306
                    "member of %(project_id)s")
 
1307
            LOG.warn(msg % locals())
 
1308
            raise ValueError(msg % locals())
 
1309
 
 
1310
        # Sanity check for storage string
 
1311
        storage_list = []
 
1312
        if storage is not None:
 
1313
            try:
 
1314
                storage_list = ast.literal_eval(storage)
 
1315
            except:
 
1316
                print _("Invalid string format %s") % storage
 
1317
                raise
 
1318
 
 
1319
            for node in storage_list:
 
1320
                if ('drive_name' not in node) or ('num_drives' not in node):
 
1321
                    print (_("Invalid string format for element %s. " \
 
1322
                            "Expecting keys 'drive_name' & 'num_drives'"),
 
1323
                            str(node))
 
1324
                    raise KeyError
 
1325
 
 
1326
        if instance_type_name == '':
 
1327
            instance_type_name = None
 
1328
        instance_type = instance_types.get_instance_type_by_name(
 
1329
                                instance_type_name)
 
1330
 
 
1331
        if image_name == '':
 
1332
            image_name = None
 
1333
 
 
1334
        if shared in [None, False, "--full_drives"]:
 
1335
            shared = False
 
1336
        elif shared in [True, "--shared"]:
 
1337
            shared = True
 
1338
        else:
 
1339
            raise ValueError(_('Shared parameter should be set either to "\
 
1340
                "--shared or --full_drives'))
 
1341
 
 
1342
        values = {
 
1343
            'display_name': name,
 
1344
            'display_description': description,
 
1345
            'vc_count': int(vc_count),
 
1346
            'instance_type': instance_type,
 
1347
            'image_name': image_name,
 
1348
            'availability_zone': az,
 
1349
            'storage': storage_list,
 
1350
            'shared': shared,
 
1351
            }
 
1352
 
 
1353
        result = self.vsa_api.create(ctxt, **values)
 
1354
        self._list(ctxt, [result])
 
1355
 
 
1356
    @args('--id', dest='vsa_id', metavar="<vsa_id>", help='VSA ID')
 
1357
    @args('--name', dest='name', metavar="<name>", help='VSA name')
 
1358
    @args('--description', dest='description', metavar="<description>",
 
1359
        help='VSA description')
 
1360
    @args('--vc', dest='vc_count', metavar="<number>", help='Number of VCs')
 
1361
    def update(self, vsa_id, name=None, description=None, vc_count=None):
 
1362
        """Updates name/description of vsa and number of VCs."""
 
1363
 
 
1364
        values = {}
 
1365
        if name is not None:
 
1366
            values['display_name'] = name
 
1367
        if description is not None:
 
1368
            values['display_description'] = description
 
1369
        if vc_count is not None:
 
1370
            values['vc_count'] = int(vc_count)
 
1371
 
 
1372
        vsa_id = ec2utils.ec2_id_to_id(vsa_id)
 
1373
        result = self.vsa_api.update(self.context, vsa_id=vsa_id, **values)
 
1374
        self._list(self.context, [result])
 
1375
 
 
1376
    @args('--id', dest='vsa_id', metavar="<vsa_id>", help='VSA ID')
 
1377
    def delete(self, vsa_id):
 
1378
        """Delete a VSA."""
 
1379
        vsa_id = ec2utils.ec2_id_to_id(vsa_id)
 
1380
        self.vsa_api.delete(self.context, vsa_id)
 
1381
 
 
1382
    @args('--id', dest='vsa_id', metavar="<vsa_id>",
 
1383
        help='VSA ID (optional)')
 
1384
    @args('--all', dest='all', action="store_true", default=False,
 
1385
        help='Show all available details')
 
1386
    @args('--drives', dest='drives', action="store_true",
 
1387
        help='Include drive-level details')
 
1388
    @args('--volumes', dest='volumes', action="store_true",
 
1389
        help='Include volume-level details')
 
1390
    @args('--instances', dest='instances', action="store_true",
 
1391
        help='Include instance-level details')
 
1392
    def list(self, vsa_id=None, all=False,
 
1393
             drives=False, volumes=False, instances=False):
 
1394
        """Describe all available VSAs (or particular one)."""
 
1395
 
 
1396
        vsas = []
 
1397
        if vsa_id is not None:
 
1398
            internal_id = ec2utils.ec2_id_to_id(vsa_id)
 
1399
            vsa = self.vsa_api.get(self.context, internal_id)
 
1400
            vsas.append(vsa)
 
1401
        else:
 
1402
            vsas = self.vsa_api.get_all(self.context)
 
1403
 
 
1404
        if all:
 
1405
            drives = volumes = instances = True
 
1406
 
 
1407
        self._list(self.context, vsas, drives, volumes, instances)
 
1408
 
 
1409
    def update_capabilities(self):
 
1410
        """Forces updates capabilities on all nova-volume nodes."""
 
1411
 
 
1412
        rpc.fanout_cast(context.get_admin_context(),
 
1413
                 FLAGS.volume_topic,
 
1414
                 {"method": "notification",
 
1415
                  "args": {"event": "startup"}})
 
1416
 
 
1417
 
 
1418
class VsaDriveTypeCommands(object):
 
1419
    """Methods for dealing with VSA drive types"""
 
1420
 
 
1421
    def __init__(self, *args, **kwargs):
 
1422
        super(VsaDriveTypeCommands, self).__init__(*args, **kwargs)
 
1423
        self.context = context.get_admin_context()
 
1424
        self._drive_type_template = '%s_%sGB_%sRPM'
 
1425
 
 
1426
    def _list(self, drives):
 
1427
        format_str = "%-5s %-30s %-10s %-10s %-10s %-20s %-10s %s"
 
1428
        if len(drives):
 
1429
            print format_str %\
 
1430
                   (_('ID'),
 
1431
                    _('name'),
 
1432
                    _('type'),
 
1433
                    _('size_gb'),
 
1434
                    _('rpm'),
 
1435
                    _('capabilities'),
 
1436
                    _('visible'),
 
1437
                    _('createTime'))
 
1438
 
 
1439
        for name, vol_type in drives.iteritems():
 
1440
            drive = vol_type.get('extra_specs')
 
1441
            print format_str %\
 
1442
                (str(vol_type['id']),
 
1443
                drive['drive_name'],
 
1444
                drive['drive_type'],
 
1445
                drive['drive_size'],
 
1446
                drive['drive_rpm'],
 
1447
                drive.get('capabilities', ''),
 
1448
                str(drive.get('visible', '')),
 
1449
                str(vol_type['created_at']))
 
1450
 
 
1451
    @args('--type', dest='type', metavar="<type>",
 
1452
        help='Drive type (SATA, SAS, SSD, etc.)')
 
1453
    @args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
 
1454
    @args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
 
1455
    @args('--capabilities', dest='capabilities', default=None,
 
1456
        metavar="<string>", help='Different capabilities')
 
1457
    @args('--hide', dest='hide', action="store_true", default=False,
 
1458
        help='Show or hide drive')
 
1459
    @args('--name', dest='name', metavar="<name>", help='Drive name')
 
1460
    def create(self, type, size_gb, rpm, capabilities=None,
 
1461
                     hide=False, name=None):
 
1462
        """Create drive type."""
 
1463
 
 
1464
        hide = True if hide in [True, "True", "--hide", "hide"] else False
 
1465
 
 
1466
        if name is None:
 
1467
            name = self._drive_type_template % (type, size_gb, rpm)
 
1468
 
 
1469
        extra_specs = {'type': 'vsa_drive',
 
1470
                       'drive_name': name,
 
1471
                       'drive_type': type,
 
1472
                       'drive_size': size_gb,
 
1473
                       'drive_rpm': rpm,
 
1474
                       'visible': True,
 
1475
                       }
 
1476
        if hide:
 
1477
            extra_specs['visible'] = False
 
1478
 
 
1479
        if capabilities is not None and capabilities != '':
 
1480
            extra_specs['capabilities'] = capabilities
 
1481
 
 
1482
        volume_types.create(self.context, name, extra_specs)
 
1483
        result = volume_types.get_volume_type_by_name(self.context, name)
 
1484
        self._list({name: result})
 
1485
 
 
1486
    @args('--name', dest='name', metavar="<name>", help='Drive name')
 
1487
    @args('--purge', action="store_true", dest='purge', default=False,
 
1488
            help='purge record from database')
 
1489
    def delete(self, name, purge):
 
1490
        """Marks instance types / flavors as deleted"""
 
1491
        try:
 
1492
            if purge:
 
1493
                volume_types.purge(self.context, name)
 
1494
                verb = "purged"
 
1495
            else:
 
1496
                volume_types.destroy(self.context, name)
 
1497
                verb = "deleted"
 
1498
        except exception.ApiError:
 
1499
            print "Valid volume type name is required"
 
1500
            sys.exit(1)
 
1501
        except exception.DBError, e:
 
1502
            print "DB Error: %s" % e
 
1503
            sys.exit(2)
 
1504
        except:
 
1505
            sys.exit(3)
 
1506
        else:
 
1507
            print "%s %s" % (name, verb)
 
1508
 
 
1509
    @args('--all', dest='all', action="store_true", default=False,
 
1510
        help='Show all drives (including invisible)')
 
1511
    @args('--name', dest='name', metavar="<name>",
 
1512
        help='Show only specified drive')
 
1513
    def list(self, all=False, name=None):
 
1514
        """Describe all available VSA drive types (or particular one)."""
 
1515
 
 
1516
        all = False if all in ["--all", False, "False"] else True
 
1517
 
 
1518
        search_opts = {'extra_specs': {'type': 'vsa_drive'}}
 
1519
        if name is not None:
 
1520
            search_opts['extra_specs']['name'] = name
 
1521
 
 
1522
        if all == False:
 
1523
            search_opts['extra_specs']['visible'] = '1'
 
1524
 
 
1525
        drives = volume_types.get_all_types(self.context,
 
1526
                                            search_opts=search_opts)
 
1527
        self._list(drives)
 
1528
 
 
1529
    @args('--name', dest='name', metavar="<name>", help='Drive name')
 
1530
    @args('--type', dest='type', metavar="<type>",
 
1531
        help='Drive type (SATA, SAS, SSD, etc.)')
 
1532
    @args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
 
1533
    @args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
 
1534
    @args('--capabilities', dest='capabilities', default=None,
 
1535
        metavar="<string>", help='Different capabilities')
 
1536
    @args('--visible', dest='visible',
 
1537
        metavar="<show|hide>", help='Show or hide drive')
 
1538
    def update(self, name, type=None, size_gb=None, rpm=None,
 
1539
                     capabilities=None, visible=None):
 
1540
        """Update drive type."""
 
1541
 
 
1542
        volume_type = volume_types.get_volume_type_by_name(self.context, name)
 
1543
 
 
1544
        extra_specs = {'type': 'vsa_drive'}
 
1545
 
 
1546
        if type:
 
1547
            extra_specs['drive_type'] = type
 
1548
 
 
1549
        if size_gb:
 
1550
            extra_specs['drive_size'] = size_gb
 
1551
 
 
1552
        if rpm:
 
1553
            extra_specs['drive_rpm'] = rpm
 
1554
 
 
1555
        if capabilities:
 
1556
            extra_specs['capabilities'] = capabilities
 
1557
 
 
1558
        if visible is not None:
 
1559
            if visible in ["show", True, "True"]:
 
1560
                extra_specs['visible'] = True
 
1561
            elif visible in ["hide", False, "False"]:
 
1562
                extra_specs['visible'] = False
 
1563
            else:
 
1564
                raise ValueError(_('visible parameter should be set to '\
 
1565
                                   'show or hide'))
 
1566
 
 
1567
        db.api.volume_type_extra_specs_update_or_create(self.context,
 
1568
                    volume_type['id'],
 
1569
                    extra_specs)
 
1570
        result = volume_types.get_volume_type_by_name(self.context, name)
 
1571
        self._list({name: result})
 
1572
 
 
1573
 
1100
1574
class VolumeCommands(object):
1101
1575
    """Methods for dealing with a cloud in an odd state"""
1102
1576
 
1483
1957
    ('agent', AgentBuildCommands),
1484
1958
    ('config', ConfigCommands),
1485
1959
    ('db', DbCommands),
 
1960
    ('drive', VsaDriveTypeCommands),
1486
1961
    ('fixed', FixedIpCommands),
1487
1962
    ('flavor', InstanceTypeCommands),
1488
1963
    ('floating', FloatingIpCommands),
1498
1973
    ('version', VersionCommands),
1499
1974
    ('vm', VmCommands),
1500
1975
    ('volume', VolumeCommands),
1501
 
    ('vpn', VpnCommands)]
 
1976
    ('vpn', VpnCommands),
 
1977
    ('vsa', VsaCommands)]
1502
1978
 
1503
1979
 
1504
1980
def lazy_match(name, key_value_tuples):