@@ -801,6 +801,7 @@ def get_parser(self, prog_name):
801801 'options.'
802802 )
803803 )
804+ # TODO(stephenfin): Remove this in the v7.0
804805 parser .add_argument (
805806 '--block-device-mapping' ,
806807 metavar = '<dev-name=mapping>' ,
@@ -809,7 +810,7 @@ def get_parser(self, prog_name):
809810 # NOTE(RuiChen): Add '\n' to the end of line to improve formatting;
810811 # see cliff's _SmartHelpFormatter for more details.
811812 help = _ (
812- 'Create a block device on the server.\n '
813+ '**Deprecated** Create a block device on the server.\n '
813814 'Block device mapping in the format\n '
814815 '<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>\n '
815816 '<dev-name>: block device name, like: vdb, xvdc '
@@ -822,6 +823,49 @@ def get_parser(self, prog_name):
822823 '(optional)\n '
823824 '<delete-on-terminate>: true or false; default: false '
824825 '(optional)\n '
826+ 'Replaced by --block-device'
827+ ),
828+ )
829+ parser .add_argument (
830+ '--block-device' ,
831+ metavar = '' ,
832+ action = parseractions .MultiKeyValueAction ,
833+ dest = 'block_devices' ,
834+ default = [],
835+ required_keys = [
836+ 'boot_index' ,
837+ ],
838+ optional_keys = [
839+ 'uuid' , 'source_type' , 'destination_type' ,
840+ 'disk_bus' , 'device_type' , 'device_name' , 'guest_format' ,
841+ 'volume_size' , 'volume_type' , 'delete_on_termination' , 'tag' ,
842+ ],
843+ help = _ (
844+ 'Create a block device on the server.\n '
845+ 'Block device in the format:\n '
846+ 'uuid=<uuid>: UUID of the volume, snapshot or ID '
847+ '(required if using source image, snapshot or volume),\n '
848+ 'source_type=<source_type>: source type '
849+ '(one of: image, snapshot, volume, blank),\n '
850+ 'destination_typ=<destination_type>: destination type '
851+ '(one of: volume, local) (optional),\n '
852+ 'disk_bus=<disk_bus>: device bus '
853+ '(one of: uml, lxc, virtio, ...) (optional),\n '
854+ 'device_type=<device_type>: device type '
855+ '(one of: disk, cdrom, etc. (optional),\n '
856+ 'device_name=<device_name>: name of the device (optional),\n '
857+ 'volume_size=<volume_size>: size of the block device in MiB '
858+ '(for swap) or GiB (for everything else) (optional),\n '
859+ 'guest_format=<guest_format>: format of device (optional),\n '
860+ 'boot_index=<boot_index>: index of disk used to order boot '
861+ 'disk '
862+ '(required for volume-backed instances),\n '
863+ 'delete_on_termination=<true|false>: whether to delete the '
864+ 'volume upon deletion of server (optional),\n '
865+ 'tag=<tag>: device metadata tag (optional),\n '
866+ 'volume_type=<volume_type>: type of volume to create (name or '
867+ 'ID) when source if blank, image or snapshot and dest is '
868+ 'volume (optional)'
825869 ),
826870 )
827871 parser .add_argument (
@@ -1250,6 +1294,8 @@ def _match_image(image_api, wanted_properties):
12501294
12511295 # Handle block device by device name order, like: vdb -> vdc -> vdd
12521296 for mapping in parsed_args .block_device_mapping :
1297+ # The 'uuid' field isn't necessarily a UUID yet; let's validate it
1298+ # just in case
12531299 if mapping ['source_type' ] == 'volume' :
12541300 volume_id = utils .find_resource (
12551301 volume_client .volumes , mapping ['uuid' ],
@@ -1279,6 +1325,77 @@ def _match_image(image_api, wanted_properties):
12791325
12801326 block_device_mapping_v2 .append (mapping )
12811327
1328+ for mapping in parsed_args .block_devices :
1329+ try :
1330+ mapping ['boot_index' ] = int (mapping ['boot_index' ])
1331+ except ValueError :
1332+ msg = _ (
1333+ 'The boot_index key of --block-device should be an '
1334+ 'integer'
1335+ )
1336+ raise exceptions .CommandError (msg )
1337+
1338+ if 'tag' in mapping and (
1339+ compute_client .api_version < api_versions .APIVersion ('2.42' )
1340+ ):
1341+ msg = _ (
1342+ '--os-compute-api-version 2.42 or greater is '
1343+ 'required to support the tag key of --block-device'
1344+ )
1345+ raise exceptions .CommandError (msg )
1346+
1347+ if 'volume_type' in mapping and (
1348+ compute_client .api_version < api_versions .APIVersion ('2.67' )
1349+ ):
1350+ msg = _ (
1351+ '--os-compute-api-version 2.67 or greater is '
1352+ 'required to support the volume_type key of --block-device'
1353+ )
1354+ raise exceptions .CommandError (msg )
1355+
1356+ if 'source_type' in mapping :
1357+ if mapping ['source_type' ] not in (
1358+ 'volume' , 'image' , 'snapshot' , 'blank' ,
1359+ ):
1360+ msg = _ (
1361+ 'The source_type key of --block-device should be one '
1362+ 'of: volume, image, snapshot, blank'
1363+ )
1364+ raise exceptions .CommandError (msg )
1365+ else :
1366+ mapping ['source_type' ] = 'blank'
1367+
1368+ if 'destination_type' in mapping :
1369+ if mapping ['destination_type' ] not in ('local' , 'volume' ):
1370+ msg = _ (
1371+ 'The destination_type key of --block-device should be '
1372+ 'one of: local, volume'
1373+ )
1374+ raise exceptions .CommandError (msg )
1375+ else :
1376+ if mapping ['source_type' ] in ('image' , 'blank' ):
1377+ mapping ['destination_type' ] = 'local'
1378+ else : # volume, snapshot
1379+ mapping ['destination_type' ] = 'volume'
1380+
1381+ if 'delete_on_termination' in mapping :
1382+ try :
1383+ value = strutils .bool_from_string (
1384+ mapping ['delete_on_termination' ], strict = True )
1385+ except ValueError :
1386+ msg = _ (
1387+ 'The delete_on_termination key of --block-device '
1388+ 'should be a boolean-like value'
1389+ )
1390+ raise exceptions .CommandError (msg )
1391+
1392+ mapping ['delete_on_termination' ] = value
1393+ else :
1394+ if mapping ['destination_type' ] == 'local' :
1395+ mapping ['delete_on_termination' ] = True
1396+
1397+ block_device_mapping_v2 .append (mapping )
1398+
12821399 nics = parsed_args .nics
12831400
12841401 if 'auto' in nics or 'none' in nics :
0 commit comments