1# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ 2# Copyright (c) 2010, Eucalyptus Systems, Inc. 3# 4# Permission is hereby granted, free of charge, to any person obtaining a 5# copy of this software and associated documentation files (the 6# "Software"), to deal in the Software without restriction, including 7# without limitation the rights to use, copy, modify, merge, publish, dis- 8# tribute, sublicense, and/or sell copies of the Software, and to permit 9# persons to whom the Software is furnished to do so, subject to the fol- 10# lowing conditions: 11# 12# The above copyright notice and this permission notice shall be included 13# in all copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 17# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21# IN THE SOFTWARE. 22 23from boto.ec2.ec2object import EC2Object, TaggedEC2Object 24from boto.ec2.blockdevicemapping import BlockDeviceMapping 25 26 27class ProductCodes(list): 28 def startElement(self, name, attrs, connection): 29 pass 30 31 def endElement(self, name, value, connection): 32 if name == 'productCode': 33 self.append(value) 34 35 36class BillingProducts(list): 37 def startElement(self, name, attrs, connection): 38 pass 39 40 def endElement(self, name, value, connection): 41 if name == 'billingProduct': 42 self.append(value) 43 44 45class Image(TaggedEC2Object): 46 """ 47 Represents an EC2 Image 48 """ 49 50 def __init__(self, connection=None): 51 super(Image, self).__init__(connection) 52 self.id = None 53 self.location = None 54 self.state = None 55 self.ownerId = None # for backwards compatibility 56 self.owner_id = None 57 self.owner_alias = None 58 self.is_public = False 59 self.architecture = None 60 self.platform = None 61 self.type = None 62 self.kernel_id = None 63 self.ramdisk_id = None 64 self.name = None 65 self.description = None 66 self.product_codes = ProductCodes() 67 self.billing_products = BillingProducts() 68 self.block_device_mapping = None 69 self.root_device_type = None 70 self.root_device_name = None 71 self.virtualization_type = None 72 self.hypervisor = None 73 self.instance_lifecycle = None 74 self.sriov_net_support = None 75 76 def __repr__(self): 77 return 'Image:%s' % self.id 78 79 def startElement(self, name, attrs, connection): 80 retval = super(Image, self).startElement(name, attrs, connection) 81 if retval is not None: 82 return retval 83 if name == 'blockDeviceMapping': 84 self.block_device_mapping = BlockDeviceMapping() 85 return self.block_device_mapping 86 elif name == 'productCodes': 87 return self.product_codes 88 elif name == 'billingProducts': 89 return self.billing_products 90 else: 91 return None 92 93 def endElement(self, name, value, connection): 94 if name == 'imageId': 95 self.id = value 96 elif name == 'imageLocation': 97 self.location = value 98 elif name == 'imageState': 99 self.state = value 100 elif name == 'imageOwnerId': 101 self.ownerId = value # for backwards compatibility 102 self.owner_id = value 103 elif name == 'isPublic': 104 if value == 'false': 105 self.is_public = False 106 elif value == 'true': 107 self.is_public = True 108 else: 109 raise Exception( 110 'Unexpected value of isPublic %s for image %s' % ( 111 value, 112 self.id 113 ) 114 ) 115 elif name == 'architecture': 116 self.architecture = value 117 elif name == 'imageType': 118 self.type = value 119 elif name == 'kernelId': 120 self.kernel_id = value 121 elif name == 'ramdiskId': 122 self.ramdisk_id = value 123 elif name == 'imageOwnerAlias': 124 self.owner_alias = value 125 elif name == 'platform': 126 self.platform = value 127 elif name == 'name': 128 self.name = value 129 elif name == 'description': 130 self.description = value 131 elif name == 'rootDeviceType': 132 self.root_device_type = value 133 elif name == 'rootDeviceName': 134 self.root_device_name = value 135 elif name == 'virtualizationType': 136 self.virtualization_type = value 137 elif name == 'hypervisor': 138 self.hypervisor = value 139 elif name == 'instanceLifecycle': 140 self.instance_lifecycle = value 141 elif name == 'sriovNetSupport': 142 self.sriov_net_support = value 143 else: 144 setattr(self, name, value) 145 146 def _update(self, updated): 147 self.__dict__.update(updated.__dict__) 148 149 def update(self, validate=False, dry_run=False): 150 """ 151 Update the image's state information by making a call to fetch 152 the current image attributes from the service. 153 154 :type validate: bool 155 :param validate: By default, if EC2 returns no data about the 156 image the update method returns quietly. If 157 the validate param is True, however, it will 158 raise a ValueError exception if no data is 159 returned from EC2. 160 """ 161 rs = self.connection.get_all_images([self.id], dry_run=dry_run) 162 if len(rs) > 0: 163 img = rs[0] 164 if img.id == self.id: 165 self._update(img) 166 elif validate: 167 raise ValueError('%s is not a valid Image ID' % self.id) 168 return self.state 169 170 def run(self, min_count=1, max_count=1, key_name=None, 171 security_groups=None, user_data=None, 172 addressing_type=None, instance_type='m1.small', placement=None, 173 kernel_id=None, ramdisk_id=None, 174 monitoring_enabled=False, subnet_id=None, 175 block_device_map=None, 176 disable_api_termination=False, 177 instance_initiated_shutdown_behavior=None, 178 private_ip_address=None, 179 placement_group=None, security_group_ids=None, 180 additional_info=None, instance_profile_name=None, 181 instance_profile_arn=None, tenancy=None, dry_run=False): 182 183 """ 184 Runs this instance. 185 186 :type min_count: int 187 :param min_count: The minimum number of instances to start 188 189 :type max_count: int 190 :param max_count: The maximum number of instances to start 191 192 :type key_name: string 193 :param key_name: The name of the key pair with which to 194 launch instances. 195 196 :type security_groups: list of strings 197 :param security_groups: The names of the security groups with which to 198 associate instances. 199 200 :type user_data: string 201 :param user_data: The Base64-encoded MIME user data to be made 202 available to the instance(s) in this reservation. 203 204 :type instance_type: string 205 :param instance_type: The type of instance to run: 206 207 * t1.micro 208 * m1.small 209 * m1.medium 210 * m1.large 211 * m1.xlarge 212 * m3.medium 213 * m3.large 214 * m3.xlarge 215 * m3.2xlarge 216 * c1.medium 217 * c1.xlarge 218 * m2.xlarge 219 * m2.2xlarge 220 * m2.4xlarge 221 * cr1.8xlarge 222 * hi1.4xlarge 223 * hs1.8xlarge 224 * cc1.4xlarge 225 * cg1.4xlarge 226 * cc2.8xlarge 227 * g2.2xlarge 228 * c3.large 229 * c3.xlarge 230 * c3.2xlarge 231 * c3.4xlarge 232 * c3.8xlarge 233 * i2.xlarge 234 * i2.2xlarge 235 * i2.4xlarge 236 * i2.8xlarge 237 * t2.micro 238 * t2.small 239 * t2.medium 240 241 :type placement: string 242 :param placement: The Availability Zone to launch the instance into. 243 244 :type kernel_id: string 245 :param kernel_id: The ID of the kernel with which to launch the 246 instances. 247 248 :type ramdisk_id: string 249 :param ramdisk_id: The ID of the RAM disk with which to launch the 250 instances. 251 252 :type monitoring_enabled: bool 253 :param monitoring_enabled: Enable CloudWatch monitoring on 254 the instance. 255 256 :type subnet_id: string 257 :param subnet_id: The subnet ID within which to launch the instances 258 for VPC. 259 260 :type private_ip_address: string 261 :param private_ip_address: If you're using VPC, you can 262 optionally use this parameter to assign the instance a 263 specific available IP address from the subnet (e.g., 264 10.0.0.25). 265 266 :type block_device_map: :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping` 267 :param block_device_map: A BlockDeviceMapping data structure 268 describing the EBS volumes associated with the Image. 269 270 :type disable_api_termination: bool 271 :param disable_api_termination: If True, the instances will be locked 272 and will not be able to be terminated via the API. 273 274 :type instance_initiated_shutdown_behavior: string 275 :param instance_initiated_shutdown_behavior: Specifies whether the 276 instance stops or terminates on instance-initiated shutdown. 277 Valid values are: 278 279 * stop 280 * terminate 281 282 :type placement_group: string 283 :param placement_group: If specified, this is the name of the placement 284 group in which the instance(s) will be launched. 285 286 :type additional_info: string 287 :param additional_info: Specifies additional information to make 288 available to the instance(s). 289 290 :type security_group_ids: list of strings 291 :param security_group_ids: The ID of the VPC security groups with 292 which to associate instances. 293 294 :type instance_profile_name: string 295 :param instance_profile_name: The name of 296 the IAM Instance Profile (IIP) to associate with the instances. 297 298 :type instance_profile_arn: string 299 :param instance_profile_arn: The Amazon resource name (ARN) of 300 the IAM Instance Profile (IIP) to associate with the instances. 301 302 :type tenancy: string 303 :param tenancy: The tenancy of the instance you want to 304 launch. An instance with a tenancy of 'dedicated' runs on 305 single-tenant hardware and can only be launched into a 306 VPC. Valid values are:"default" or "dedicated". 307 NOTE: To use dedicated tenancy you MUST specify a VPC 308 subnet-ID as well. 309 310 :rtype: Reservation 311 :return: The :class:`boto.ec2.instance.Reservation` associated with 312 the request for machines 313 314 """ 315 316 return self.connection.run_instances(self.id, min_count, max_count, 317 key_name, security_groups, 318 user_data, addressing_type, 319 instance_type, placement, 320 kernel_id, ramdisk_id, 321 monitoring_enabled, subnet_id, 322 block_device_map, disable_api_termination, 323 instance_initiated_shutdown_behavior, 324 private_ip_address, placement_group, 325 security_group_ids=security_group_ids, 326 additional_info=additional_info, 327 instance_profile_name=instance_profile_name, 328 instance_profile_arn=instance_profile_arn, 329 tenancy=tenancy, dry_run=dry_run) 330 331 def deregister(self, delete_snapshot=False, dry_run=False): 332 return self.connection.deregister_image( 333 self.id, 334 delete_snapshot, 335 dry_run=dry_run 336 ) 337 338 def get_launch_permissions(self, dry_run=False): 339 img_attrs = self.connection.get_image_attribute( 340 self.id, 341 'launchPermission', 342 dry_run=dry_run 343 ) 344 return img_attrs.attrs 345 346 def set_launch_permissions(self, user_ids=None, group_names=None, 347 dry_run=False): 348 return self.connection.modify_image_attribute(self.id, 349 'launchPermission', 350 'add', 351 user_ids, 352 group_names, 353 dry_run=dry_run) 354 355 def remove_launch_permissions(self, user_ids=None, group_names=None, 356 dry_run=False): 357 return self.connection.modify_image_attribute(self.id, 358 'launchPermission', 359 'remove', 360 user_ids, 361 group_names, 362 dry_run=dry_run) 363 364 def reset_launch_attributes(self, dry_run=False): 365 return self.connection.reset_image_attribute( 366 self.id, 367 'launchPermission', 368 dry_run=dry_run 369 ) 370 371 def get_kernel(self, dry_run=False): 372 img_attrs = self.connection.get_image_attribute( 373 self.id, 374 'kernel', 375 dry_run=dry_run 376 ) 377 return img_attrs.kernel 378 379 def get_ramdisk(self, dry_run=False): 380 img_attrs = self.connection.get_image_attribute( 381 self.id, 382 'ramdisk', 383 dry_run=dry_run 384 ) 385 return img_attrs.ramdisk 386 387 388class ImageAttribute(object): 389 def __init__(self, parent=None): 390 self.name = None 391 self.kernel = None 392 self.ramdisk = None 393 self.attrs = {} 394 395 def startElement(self, name, attrs, connection): 396 if name == 'blockDeviceMapping': 397 self.attrs['block_device_mapping'] = BlockDeviceMapping() 398 return self.attrs['block_device_mapping'] 399 else: 400 return None 401 402 def endElement(self, name, value, connection): 403 if name == 'launchPermission': 404 self.name = 'launch_permission' 405 elif name == 'group': 406 if 'groups' in self.attrs: 407 self.attrs['groups'].append(value) 408 else: 409 self.attrs['groups'] = [value] 410 elif name == 'userId': 411 if 'user_ids' in self.attrs: 412 self.attrs['user_ids'].append(value) 413 else: 414 self.attrs['user_ids'] = [value] 415 elif name == 'productCode': 416 if 'product_codes' in self.attrs: 417 self.attrs['product_codes'].append(value) 418 else: 419 self.attrs['product_codes'] = [value] 420 elif name == 'imageId': 421 self.image_id = value 422 elif name == 'kernel': 423 self.kernel = value 424 elif name == 'ramdisk': 425 self.ramdisk = value 426 else: 427 setattr(self, name, value) 428 429 430class CopyImage(object): 431 def __init__(self, parent=None): 432 self._parent = parent 433 self.image_id = None 434 435 def startElement(self, name, attrs, connection): 436 pass 437 438 def endElement(self, name, value, connection): 439 if name == 'imageId': 440 self.image_id = value 441