@@ -772,6 +772,19 @@ def get_parser(self, prog_name):
772772 'volume.'
773773 ),
774774 )
775+ disk_group .add_argument (
776+ '--snapshot' ,
777+ metavar = '<snapshot>' ,
778+ help = _ (
779+ 'Create server using this snapshot as the boot disk (name or '
780+ 'ID)\n '
781+ 'This option automatically creates a block device mapping '
782+ 'with a boot index of 0. On many hypervisors (libvirt/kvm '
783+ 'for example) this will be device vda. Do not create a '
784+ 'duplicate mapping using --block-device-mapping for this '
785+ 'volume.'
786+ ),
787+ )
775788 parser .add_argument (
776789 '--boot-from-volume' ,
777790 metavar = '<volume-size>' ,
@@ -784,7 +797,8 @@ def get_parser(self, prog_name):
784797 'given size (in GB) from the specified image and use it '
785798 'as the root disk of the server. The root volume will not '
786799 'be deleted when the server is deleted. This option is '
787- 'mutually exclusive with the ``--volume`` option.'
800+ 'mutually exclusive with the ``--volume`` and ``--snapshot`` '
801+ 'options.'
788802 )
789803 )
790804 parser .add_argument (
@@ -810,6 +824,28 @@ def get_parser(self, prog_name):
810824 '(optional)\n '
811825 ),
812826 )
827+ parser .add_argument (
828+ '--swap' ,
829+ metavar = '<swap>' ,
830+ type = int ,
831+ help = (
832+ "Create and attach a local swap block device of <swap_size> "
833+ "MiB."
834+ ),
835+ )
836+ parser .add_argument (
837+ '--ephemeral' ,
838+ metavar = '<size=size[,format=format]>' ,
839+ action = parseractions .MultiKeyValueAction ,
840+ dest = 'ephemerals' ,
841+ default = [],
842+ required_keys = ['size' ],
843+ optional_keys = ['format' ],
844+ help = (
845+ "Create and attach a local ephemeral block device of <size> "
846+ "GiB and format it to <format>."
847+ ),
848+ )
813849 parser .add_argument (
814850 '--network' ,
815851 metavar = "<network>" ,
@@ -929,12 +965,14 @@ def get_parser(self, prog_name):
929965 parser .add_argument (
930966 '--availability-zone' ,
931967 metavar = '<zone-name>' ,
932- help = _ ('Select an availability zone for the server. '
933- 'Host and node are optional parameters. '
934- 'Availability zone in the format '
935- '<zone-name>:<host-name>:<node-name>, '
936- '<zone-name>::<node-name>, <zone-name>:<host-name> '
937- 'or <zone-name>' ),
968+ help = _ (
969+ 'Select an availability zone for the server. '
970+ 'Host and node are optional parameters. '
971+ 'Availability zone in the format '
972+ '<zone-name>:<host-name>:<node-name>, '
973+ '<zone-name>::<node-name>, <zone-name>:<host-name> '
974+ 'or <zone-name>'
975+ ),
938976 )
939977 parser .add_argument (
940978 '--host' ,
@@ -1000,11 +1038,6 @@ def get_parser(self, prog_name):
10001038 default = 1 ,
10011039 help = _ ('Maximum number of servers to launch (default=1)' ),
10021040 )
1003- parser .add_argument (
1004- '--wait' ,
1005- action = 'store_true' ,
1006- help = _ ('Wait for build to complete' ),
1007- )
10081041 parser .add_argument (
10091042 '--tag' ,
10101043 metavar = '<tag>' ,
@@ -1017,6 +1050,11 @@ def get_parser(self, prog_name):
10171050 '(supported by --os-compute-api-version 2.52 or above)'
10181051 ),
10191052 )
1053+ parser .add_argument (
1054+ '--wait' ,
1055+ action = 'store_true' ,
1056+ help = _ ('Wait for build to complete' ),
1057+ )
10201058 return parser
10211059
10221060 def take_action (self , parsed_args ):
@@ -1092,7 +1130,6 @@ def _match_image(image_api, wanted_properties):
10921130 )
10931131 raise exceptions .CommandError (msg )
10941132
1095- # Lookup parsed_args.volume
10961133 volume = None
10971134 if parsed_args .volume :
10981135 # --volume and --boot-from-volume are mutually exclusive.
@@ -1105,7 +1142,18 @@ def _match_image(image_api, wanted_properties):
11051142 parsed_args .volume ,
11061143 ).id
11071144
1108- # Lookup parsed_args.flavor
1145+ snapshot = None
1146+ if parsed_args .snapshot :
1147+ # --snapshot and --boot-from-volume are mutually exclusive.
1148+ if parsed_args .boot_from_volume :
1149+ msg = _ ('--snapshot is not allowed with --boot-from-volume' )
1150+ raise exceptions .CommandError (msg )
1151+
1152+ snapshot = utils .find_resource (
1153+ volume_client .volume_snapshots ,
1154+ parsed_args .snapshot ,
1155+ ).id
1156+
11091157 flavor = utils .find_resource (
11101158 compute_client .flavors , parsed_args .flavor )
11111159
@@ -1156,6 +1204,14 @@ def _match_image(image_api, wanted_properties):
11561204 'source_type' : 'volume' ,
11571205 'destination_type' : 'volume'
11581206 }]
1207+ elif snapshot :
1208+ block_device_mapping_v2 = [{
1209+ 'uuid' : snapshot ,
1210+ 'boot_index' : '0' ,
1211+ 'source_type' : 'snapshot' ,
1212+ 'destination_type' : 'volume' ,
1213+ 'delete_on_termination' : False
1214+ }]
11591215 elif parsed_args .boot_from_volume :
11601216 # Tell nova to create a root volume from the image provided.
11611217 block_device_mapping_v2 = [{
@@ -1168,6 +1224,30 @@ def _match_image(image_api, wanted_properties):
11681224 # If booting from volume we do not pass an image to compute.
11691225 image = None
11701226
1227+ if parsed_args .swap :
1228+ block_device_mapping_v2 .append ({
1229+ 'boot_index' : - 1 ,
1230+ 'source_type' : 'blank' ,
1231+ 'destination_type' : 'local' ,
1232+ 'guest_format' : 'swap' ,
1233+ 'volume_size' : parsed_args .swap ,
1234+ 'delete_on_termination' : True ,
1235+ })
1236+
1237+ for mapping in parsed_args .ephemerals :
1238+ block_device_mapping_dict = {
1239+ 'boot_index' : - 1 ,
1240+ 'source_type' : 'blank' ,
1241+ 'destination_type' : 'local' ,
1242+ 'delete_on_termination' : True ,
1243+ 'volume_size' : mapping ['size' ],
1244+ }
1245+
1246+ if 'format' in mapping :
1247+ block_device_mapping_dict ['guest_format' ] = mapping ['format' ]
1248+
1249+ block_device_mapping_v2 .append (block_device_mapping_dict )
1250+
11711251 # Handle block device by device name order, like: vdb -> vdc -> vdd
11721252 for mapping in parsed_args .block_device_mapping :
11731253 if mapping ['source_type' ] == 'volume' :
0 commit comments