1import base64 2import hashlib 3import fnmatch 4import random 5import re 6import ipaddress 7 8from cryptography.hazmat.primitives import serialization 9from cryptography.hazmat.backends import default_backend 10from cryptography.hazmat.primitives.asymmetric import rsa 11 12from moto.core import ACCOUNT_ID 13from moto.iam import iam_backends 14 15EC2_RESOURCE_TO_PREFIX = { 16 "customer-gateway": "cgw", 17 "transit-gateway": "tgw", 18 "transit-gateway-route-table": "tgw-rtb", 19 "transit-gateway-attachment": "tgw-attach", 20 "dhcp-options": "dopt", 21 "flow-logs": "fl", 22 "image": "ami", 23 "instance": "i", 24 "internet-gateway": "igw", 25 "egress-only-internet-gateway": "eigw", 26 "launch-template": "lt", 27 "nat-gateway": "nat", 28 "network-acl": "acl", 29 "network-acl-subnet-assoc": "aclassoc", 30 "network-interface": "eni", 31 "network-interface-attachment": "eni-attach", 32 "reserved-instance": "uuid4", 33 "route-table": "rtb", 34 "route-table-association": "rtbassoc", 35 "security-group": "sg", 36 "security-group-rule": "sgr", 37 "snapshot": "snap", 38 "spot-instance-request": "sir", 39 "spot-fleet-request": "sfr", 40 "subnet": "subnet", 41 "subnet-ipv6-cidr-block-association": "subnet-cidr-assoc", 42 "reservation": "r", 43 "volume": "vol", 44 "vpc": "vpc", 45 "vpc-endpoint": "vpce", 46 "managed-prefix-list": "pl", 47 "vpc-cidr-association-id": "vpc-cidr-assoc", 48 "vpc-elastic-ip": "eipalloc", 49 "vpc-elastic-ip-association": "eipassoc", 50 "vpc-peering-connection": "pcx", 51 "vpn-connection": "vpn", 52 "vpn-gateway": "vgw", 53 "iam-instance-profile-association": "iip-assoc", 54 "carrier-gateway": "cagw", 55} 56 57 58EC2_PREFIX_TO_RESOURCE = dict((v, k) for (k, v) in EC2_RESOURCE_TO_PREFIX.items()) 59 60 61def random_resource_id(size=8): 62 chars = list(range(10)) + ["a", "b", "c", "d", "e", "f"] 63 resource_id = "".join(str(random.choice(chars)) for _ in range(size)) 64 return resource_id 65 66 67def random_id(prefix="", size=8): 68 return "{0}-{1}".format(prefix, random_resource_id(size)) 69 70 71def random_ami_id(): 72 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["image"]) 73 74 75def random_instance_id(): 76 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["instance"], size=17) 77 78 79def random_reservation_id(): 80 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["reservation"]) 81 82 83def random_security_group_id(): 84 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group"], size=17) 85 86 87def random_security_group_rule_id(): 88 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["security-group-rule"], size=17) 89 90 91def random_flow_log_id(): 92 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["flow-logs"]) 93 94 95def random_snapshot_id(): 96 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["snapshot"]) 97 98 99def random_spot_request_id(): 100 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["spot-instance-request"]) 101 102 103def random_spot_fleet_request_id(): 104 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["spot-fleet-request"]) 105 106 107def random_subnet_id(): 108 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["subnet"]) 109 110 111def random_subnet_ipv6_cidr_block_association_id(): 112 return random_id( 113 prefix=EC2_RESOURCE_TO_PREFIX["subnet-ipv6-cidr-block-association"] 114 ) 115 116 117def random_subnet_association_id(): 118 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["route-table-association"]) 119 120 121def random_network_acl_id(): 122 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["network-acl"]) 123 124 125def random_network_acl_subnet_association_id(): 126 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["network-acl-subnet-assoc"]) 127 128 129def random_vpn_gateway_id(): 130 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpn-gateway"]) 131 132 133def random_vpn_connection_id(): 134 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpn-connection"]) 135 136 137def random_customer_gateway_id(): 138 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["customer-gateway"]) 139 140 141def random_volume_id(): 142 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["volume"]) 143 144 145def random_vpc_id(): 146 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc"]) 147 148 149def random_vpc_ep_id(): 150 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-endpoint"], size=8) 151 152 153def random_vpc_cidr_association_id(): 154 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-cidr-association-id"]) 155 156 157def random_vpc_peering_connection_id(): 158 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-peering-connection"]) 159 160 161def random_eip_association_id(): 162 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-elastic-ip-association"]) 163 164 165def random_internet_gateway_id(): 166 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["internet-gateway"]) 167 168 169def random_egress_only_internet_gateway_id(): 170 return random_id( 171 prefix=EC2_RESOURCE_TO_PREFIX["egress-only-internet-gateway"], size=17 172 ) 173 174 175def random_route_table_id(): 176 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["route-table"]) 177 178 179def random_eip_allocation_id(): 180 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["vpc-elastic-ip"]) 181 182 183def random_dhcp_option_id(): 184 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["dhcp-options"]) 185 186 187def random_eni_id(): 188 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["network-interface"]) 189 190 191def random_eni_attach_id(): 192 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["network-interface-attachment"]) 193 194 195def random_nat_gateway_id(): 196 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["nat-gateway"], size=17) 197 198 199def random_transit_gateway_id(): 200 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway"], size=17) 201 202 203def random_transit_gateway_route_table_id(): 204 return random_id( 205 prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-route-table"], size=17 206 ) 207 208 209def random_transit_gateway_attachment_id(): 210 return random_id( 211 prefix=EC2_RESOURCE_TO_PREFIX["transit-gateway-attachment"], size=17 212 ) 213 214 215def random_launch_template_id(): 216 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["launch-template"], size=17) 217 218 219def random_iam_instance_profile_association_id(): 220 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["iam-instance-profile-association"]) 221 222 223def random_carrier_gateway_id(): 224 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["carrier-gateway"], size=17) 225 226 227def random_public_ip(): 228 return "54.214.{0}.{1}".format(random.choice(range(255)), random.choice(range(255))) 229 230 231def random_private_ip(cidr=None, ipv6=False): 232 # prefix - ula.prefixlen : get number of remaing length for the IP. 233 # prefix will be 32 for IPv4 and 128 for IPv6. 234 # random.getrandbits() will generate remaining bits for IPv6 or Ipv4 in decimal format 235 if cidr: 236 if ipv6: 237 ula = ipaddress.IPv6Network(cidr) 238 return str(ula.network_address + (random.getrandbits(128 - ula.prefixlen))) 239 ula = ipaddress.IPv4Network(cidr) 240 return str(ula.network_address + (random.getrandbits(32 - ula.prefixlen))) 241 if ipv6: 242 return "2001::cafe:%x/64" % random.getrandbits(16) 243 return "10.{0}.{1}.{2}".format( 244 random.choice(range(255)), random.choice(range(255)), random.choice(range(255)) 245 ) 246 247 248def random_ip(): 249 return "127.{0}.{1}.{2}".format( 250 random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) 251 ) 252 253 254def generate_dns_from_ip(ip, type="internal"): 255 splits = ip.split("/")[0].split(".") if "/" in ip else ip.split(".") 256 return "ip-{}-{}-{}-{}.ec2.{}".format( 257 splits[0], splits[1], splits[2], splits[3], type 258 ) 259 260 261def random_mac_address(): 262 return "02:00:00:%02x:%02x:%02x" % ( 263 random.randint(0, 255), 264 random.randint(0, 255), 265 random.randint(0, 255), 266 ) 267 268 269def randor_ipv4_cidr(): 270 return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255)) 271 272 273def random_ipv6_cidr(): 274 return "2400:6500:{}:{}00::/56".format(random_resource_id(4), random_resource_id(2)) 275 276 277def generate_route_id( 278 route_table_id, cidr_block, ipv6_cidr_block=None, prefix_list=None 279): 280 if ipv6_cidr_block and not cidr_block: 281 cidr_block = ipv6_cidr_block 282 if prefix_list and not cidr_block: 283 cidr_block = prefix_list 284 return "%s~%s" % (route_table_id, cidr_block) 285 286 287def random_managed_prefix_list_id(): 288 return random_id(prefix=EC2_RESOURCE_TO_PREFIX["managed-prefix-list"], size=8) 289 290 291def create_dns_entries(service_name, vpc_endpoint_id): 292 dns_entries = {} 293 dns_entries["dns_name"] = "{}-{}.{}".format( 294 vpc_endpoint_id, random_resource_id(8), service_name 295 ) 296 dns_entries["hosted_zone_id"] = random_resource_id(13).upper() 297 return dns_entries 298 299 300def split_route_id(route_id): 301 values = route_id.split("~") 302 return values[0], values[1] 303 304 305def dhcp_configuration_from_querystring(querystring, option="DhcpConfiguration"): 306 """ 307 turn: 308 {u'AWSAccessKeyId': [u'the_key'], 309 u'Action': [u'CreateDhcpOptions'], 310 u'DhcpConfiguration.1.Key': [u'domain-name'], 311 u'DhcpConfiguration.1.Value.1': [u'example.com'], 312 u'DhcpConfiguration.2.Key': [u'domain-name-servers'], 313 u'DhcpConfiguration.2.Value.1': [u'10.0.0.6'], 314 u'DhcpConfiguration.2.Value.2': [u'10.0.0.7'], 315 u'Signature': [u'uUMHYOoLM6r+sT4fhYjdNT6MHw22Wj1mafUpe0P0bY4='], 316 u'SignatureMethod': [u'HmacSHA256'], 317 u'SignatureVersion': [u'2'], 318 u'Timestamp': [u'2014-03-18T21:54:01Z'], 319 u'Version': [u'2013-10-15']} 320 into: 321 {u'domain-name': [u'example.com'], u'domain-name-servers': [u'10.0.0.6', u'10.0.0.7']} 322 """ 323 324 key_needle = re.compile("{0}.[0-9]+.Key".format(option), re.UNICODE) 325 response_values = {} 326 327 for key, value in querystring.items(): 328 if key_needle.match(key): 329 values = [] 330 key_index = key.split(".")[1] 331 value_index = 1 332 while True: 333 value_key = "{0}.{1}.Value.{2}".format(option, key_index, value_index) 334 if value_key in querystring: 335 values.extend(querystring[value_key]) 336 else: 337 break 338 value_index += 1 339 response_values[value[0]] = values 340 return response_values 341 342 343def filters_from_querystring(querystring_dict): 344 response_values = {} 345 last_tag_key = None 346 for key, value in sorted(querystring_dict.items()): 347 match = re.search(r"Filter.(\d).Name", key) 348 if match: 349 filter_index = match.groups()[0] 350 value_prefix = "Filter.{0}.Value".format(filter_index) 351 filter_values = [ 352 filter_value[0] 353 for filter_key, filter_value in querystring_dict.items() 354 if filter_key.startswith(value_prefix) 355 ] 356 if value[0] == "tag-key": 357 last_tag_key = "tag:" + filter_values[0] 358 elif last_tag_key and value[0] == "tag-value": 359 response_values[last_tag_key] = filter_values 360 response_values[value[0]] = filter_values 361 return response_values 362 363 364def dict_from_querystring(parameter, querystring_dict): 365 use_dict = {} 366 for key, value in querystring_dict.items(): 367 match = re.search(r"{0}.(\d).(\w+)".format(parameter), key) 368 if match: 369 use_dict_index = match.groups()[0] 370 use_dict_element_property = match.groups()[1] 371 372 if not use_dict.get(use_dict_index): 373 use_dict[use_dict_index] = {} 374 use_dict[use_dict_index][use_dict_element_property] = value[0] 375 376 return use_dict 377 378 379def get_attribute_value(parameter, querystring_dict): 380 for key, value in querystring_dict.items(): 381 match = re.search(r"{0}.Value".format(parameter), key) 382 if match: 383 if value[0].lower() in ["true", "false"]: 384 return True if value[0].lower() in ["true"] else False 385 return value[0] 386 return None 387 388 389def get_object_value(obj, attr): 390 keys = attr.split(".") 391 val = obj 392 for key in keys: 393 if key == "owner_id": 394 return ACCOUNT_ID 395 elif hasattr(val, key): 396 val = getattr(val, key) 397 elif isinstance(val, dict): 398 val = val[key] 399 elif isinstance(val, list): 400 for item in val: 401 item_val = get_object_value(item, key) 402 if item_val: 403 return item_val 404 else: 405 return None 406 return val 407 408 409def is_tag_filter(filter_name): 410 return ( 411 filter_name.startswith("tag:") 412 or filter_name.startswith("tag-value") 413 or filter_name.startswith("tag-key") 414 ) 415 416 417def get_obj_tag(obj, filter_name): 418 tag_name = filter_name.replace("tag:", "", 1) 419 tags = dict((tag["key"], tag["value"]) for tag in obj.get_tags()) 420 return tags.get(tag_name) 421 422 423def get_obj_tag_names(obj): 424 tags = set((tag["key"] for tag in obj.get_tags())) 425 return tags 426 427 428def get_obj_tag_values(obj, key=None): 429 tags = set( 430 (tag["value"] for tag in obj.get_tags() if tag["key"] == key or key is None) 431 ) 432 return tags 433 434 435def add_tag_specification(tags): 436 tags = tags[0] if isinstance(tags, list) and len(tags) == 1 else tags 437 tags = (tags or {}).get("Tag", []) 438 tags = {t["Key"]: t["Value"] for t in tags} 439 return tags 440 441 442def tag_filter_matches(obj, filter_name, filter_values): 443 regex_filters = [re.compile(simple_aws_filter_to_re(f)) for f in filter_values] 444 if filter_name == "tag-key": 445 tag_values = get_obj_tag_names(obj) 446 elif filter_name == "tag-value": 447 tag_values = get_obj_tag_values(obj) 448 elif filter_name.startswith("tag:"): 449 key = filter_name[4:] 450 tag_values = get_obj_tag_values(obj, key=key) 451 else: 452 tag_values = [get_obj_tag(obj, filter_name) or ""] 453 454 for tag_value in tag_values: 455 if any(regex.match(tag_value) for regex in regex_filters): 456 return True 457 458 return False 459 460 461filter_dict_attribute_mapping = { 462 "instance-state-name": "state", 463 "instance-id": "id", 464 "state-reason-code": "_state_reason.code", 465 "source-dest-check": "source_dest_check", 466 "vpc-id": "vpc_id", 467 "group-id": "security_groups.id", 468 "instance.group-id": "security_groups.id", 469 "instance.group-name": "security_groups.name", 470 "instance-type": "instance_type", 471 "private-ip-address": "private_ip", 472 "ip-address": "public_ip", 473 "availability-zone": "placement", 474 "architecture": "architecture", 475 "image-id": "image_id", 476 "network-interface.private-dns-name": "private_dns", 477 "private-dns-name": "private_dns", 478 "owner-id": "owner_id", 479 "subnet-id": "subnet_id", 480} 481 482 483def passes_filter_dict(instance, filter_dict): 484 for filter_name, filter_values in filter_dict.items(): 485 if filter_name in filter_dict_attribute_mapping: 486 instance_attr = filter_dict_attribute_mapping[filter_name] 487 instance_value = get_object_value(instance, instance_attr) 488 if not instance_value_in_filter_values(instance_value, filter_values): 489 return False 490 491 elif is_tag_filter(filter_name): 492 if not tag_filter_matches(instance, filter_name, filter_values): 493 return False 494 else: 495 raise NotImplementedError( 496 "Filter dicts have not been implemented in Moto for '%s' yet. Feel free to open an issue at https://github.com/spulec/moto/issues" 497 % filter_name 498 ) 499 return True 500 501 502def instance_value_in_filter_values(instance_value, filter_values): 503 if isinstance(instance_value, list): 504 if not set(filter_values).intersection(set(instance_value)): 505 return False 506 elif instance_value not in filter_values: 507 return False 508 return True 509 510 511def filter_reservations(reservations, filter_dict): 512 result = [] 513 for reservation in reservations: 514 new_instances = [] 515 for instance in reservation.instances: 516 if passes_filter_dict(instance, filter_dict): 517 new_instances.append(instance) 518 if new_instances: 519 reservation.instances = new_instances 520 result.append(reservation) 521 return result 522 523 524filter_dict_igw_mapping = { 525 "attachment.vpc-id": "vpc.id", 526 "attachment.state": "attachment_state", 527 "internet-gateway-id": "id", 528} 529 530 531def passes_igw_filter_dict(igw, filter_dict): 532 for filter_name, filter_values in filter_dict.items(): 533 if filter_name in filter_dict_igw_mapping: 534 igw_attr = filter_dict_igw_mapping[filter_name] 535 if get_object_value(igw, igw_attr) not in filter_values: 536 return False 537 elif is_tag_filter(filter_name): 538 if not tag_filter_matches(igw, filter_name, filter_values): 539 return False 540 else: 541 raise NotImplementedError( 542 "Internet Gateway filter dicts have not been implemented in Moto for '%s' yet. Feel free to open an issue at https://github.com/spulec/moto/issues", 543 filter_name, 544 ) 545 return True 546 547 548def filter_internet_gateways(igws, filter_dict): 549 result = [] 550 for igw in igws: 551 if passes_igw_filter_dict(igw, filter_dict): 552 result.append(igw) 553 return result 554 555 556def is_filter_matching(obj, filter, filter_value): 557 value = obj.get_filter_value(filter) 558 559 if filter_value is None: 560 return False 561 562 if isinstance(value, str): 563 if not isinstance(filter_value, list): 564 filter_value = [filter_value] 565 if any(fnmatch.fnmatch(value, pattern) for pattern in filter_value): 566 return True 567 return False 568 569 if isinstance(value, type({}.keys())): 570 if isinstance(filter_value, str) and filter_value in value: 571 return True 572 573 try: 574 value = set(value) 575 return (value and value.issubset(filter_value)) or value.issuperset( 576 filter_value 577 ) 578 except TypeError: 579 return value in filter_value 580 581 582def generic_filter(filters, objects): 583 if filters: 584 for (_filter, _filter_value) in filters.items(): 585 objects = [ 586 obj 587 for obj in objects 588 if is_filter_matching(obj, _filter, _filter_value) 589 ] 590 591 return objects 592 593 594def simple_aws_filter_to_re(filter_string): 595 tmp_filter = filter_string.replace(r"\?", "[?]") 596 tmp_filter = tmp_filter.replace(r"\*", "[*]") 597 tmp_filter = fnmatch.translate(tmp_filter) 598 return tmp_filter 599 600 601def random_key_pair(): 602 private_key = rsa.generate_private_key( 603 public_exponent=65537, key_size=2048, backend=default_backend() 604 ) 605 private_key_material = private_key.private_bytes( 606 encoding=serialization.Encoding.PEM, 607 format=serialization.PrivateFormat.TraditionalOpenSSL, 608 encryption_algorithm=serialization.NoEncryption(), 609 ) 610 public_key_fingerprint = rsa_public_key_fingerprint(private_key.public_key()) 611 612 return { 613 "fingerprint": public_key_fingerprint, 614 "material": private_key_material.decode("ascii"), 615 } 616 617 618def get_prefix(resource_id): 619 resource_id_prefix, separator, after = resource_id.partition("-") 620 if resource_id_prefix == EC2_RESOURCE_TO_PREFIX["transit-gateway"]: 621 if after.startswith("rtb"): 622 resource_id_prefix = EC2_RESOURCE_TO_PREFIX["transit-gateway-route-table"] 623 if after.startswith("attach"): 624 resource_id_prefix = EC2_RESOURCE_TO_PREFIX["transit-gateway-attachment"] 625 if resource_id_prefix == EC2_RESOURCE_TO_PREFIX["network-interface"]: 626 if after.startswith("attach"): 627 resource_id_prefix = EC2_RESOURCE_TO_PREFIX["network-interface-attachment"] 628 if resource_id_prefix not in EC2_RESOURCE_TO_PREFIX.values(): 629 uuid4hex = re.compile(r"[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}\Z", re.I) 630 if uuid4hex.match(resource_id) is not None: 631 resource_id_prefix = EC2_RESOURCE_TO_PREFIX["reserved-instance"] 632 else: 633 return None 634 return resource_id_prefix 635 636 637def is_valid_resource_id(resource_id): 638 valid_prefixes = EC2_RESOURCE_TO_PREFIX.values() 639 resource_id_prefix = get_prefix(resource_id) 640 if resource_id_prefix not in valid_prefixes: 641 return False 642 resource_id_pattern = resource_id_prefix + "-[0-9a-f]{8}" 643 resource_pattern_re = re.compile(resource_id_pattern) 644 return resource_pattern_re.match(resource_id) is not None 645 646 647def is_valid_cidr(cird): 648 cidr_pattern = r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$" 649 cidr_pattern_re = re.compile(cidr_pattern) 650 return cidr_pattern_re.match(cird) is not None 651 652 653def is_valid_ipv6_cidr(cird): 654 cidr_pattern = r"^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$" 655 cidr_pattern_re = re.compile(cidr_pattern) 656 return cidr_pattern_re.match(cird) is not None 657 658 659def generate_instance_identity_document(instance): 660 """ 661 http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html 662 663 A JSON file that describes an instance. Usually retrieved by URL: 664 http://169.254.169.254/latest/dynamic/instance-identity/document 665 Here we just fill a dictionary that represents the document 666 667 Typically, this document is used by the amazon-ecs-agent when registering a 668 new ContainerInstance 669 """ 670 671 document = { 672 "devPayProductCodes": None, 673 "availabilityZone": instance.placement["AvailabilityZone"], 674 "privateIp": instance.private_ip_address, 675 "version": "2010-8-31", 676 "region": instance.placement["AvailabilityZone"][:-1], 677 "instanceId": instance.id, 678 "billingProducts": None, 679 "instanceType": instance.instance_type, 680 "accountId": "012345678910", 681 "pendingTime": "2015-11-19T16:32:11Z", 682 "imageId": instance.image_id, 683 "kernelId": instance.kernel_id, 684 "ramdiskId": instance.ramdisk_id, 685 "architecture": instance.architecture, 686 } 687 688 return document 689 690 691def rsa_public_key_parse(key_material): 692 # These imports take ~.5s; let's keep them local 693 import sshpubkeys.exceptions 694 from sshpubkeys.keys import SSHKey 695 696 try: 697 if not isinstance(key_material, bytes): 698 key_material = key_material.encode("ascii") 699 700 decoded_key = base64.b64decode(key_material).decode("ascii") 701 public_key = SSHKey(decoded_key) 702 except (sshpubkeys.exceptions.InvalidKeyException, UnicodeDecodeError): 703 raise ValueError("bad key") 704 705 if not public_key.rsa: 706 raise ValueError("bad key") 707 708 return public_key.rsa 709 710 711def rsa_public_key_fingerprint(rsa_public_key): 712 key_data = rsa_public_key.public_bytes( 713 encoding=serialization.Encoding.DER, 714 format=serialization.PublicFormat.SubjectPublicKeyInfo, 715 ) 716 fingerprint_hex = hashlib.md5(key_data).hexdigest() 717 fingerprint = re.sub(r"([a-f0-9]{2})(?!$)", r"\1:", fingerprint_hex) 718 return fingerprint 719 720 721def filter_iam_instance_profile_associations(iam_instance_associations, filter_dict): 722 if not filter_dict: 723 return iam_instance_associations 724 result = [] 725 for iam_instance_association in iam_instance_associations: 726 filter_passed = True 727 if filter_dict.get("instance-id"): 728 if ( 729 iam_instance_association.instance.id 730 not in filter_dict.get("instance-id").values() 731 ): 732 filter_passed = False 733 if filter_dict.get("state"): 734 if iam_instance_association.state not in filter_dict.get("state").values(): 735 filter_passed = False 736 if filter_passed: 737 result.append(iam_instance_association) 738 return result 739 740 741def filter_iam_instance_profiles(iam_instance_profile_arn, iam_instance_profile_name): 742 instance_profile = None 743 instance_profile_by_name = None 744 instance_profile_by_arn = None 745 if iam_instance_profile_name: 746 instance_profile_by_name = iam_backends["global"].get_instance_profile( 747 iam_instance_profile_name 748 ) 749 instance_profile = instance_profile_by_name 750 if iam_instance_profile_arn: 751 instance_profile_by_arn = iam_backends["global"].get_instance_profile_by_arn( 752 iam_instance_profile_arn 753 ) 754 instance_profile = instance_profile_by_arn 755 # We would prefer instance profile that we found by arn 756 if iam_instance_profile_arn and iam_instance_profile_name: 757 if instance_profile_by_name == instance_profile_by_arn: 758 instance_profile = instance_profile_by_arn 759 else: 760 instance_profile = None 761 762 return instance_profile 763 764 765def describe_tag_filter(filters, instances): 766 result = instances.copy() 767 for instance in instances: 768 for key in filters: 769 if key.startswith("tag:"): 770 match = re.match(r"tag:(.*)", key) 771 if match: 772 tag_key_name = match.group(1) 773 need_delete = True 774 for tag in instance.get_tags(): 775 if tag.get("key") == tag_key_name and tag.get( 776 "value" 777 ) in filters.get(key): 778 need_delete = False 779 elif tag.get("key") == tag_key_name and tag.get( 780 "value" 781 ) not in filters.get(key): 782 need_delete = True 783 if need_delete: 784 result.remove(instance) 785 return result 786