Files
ipxe/contrib/cloud/ali-setup
T

344 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import argparse
from collections import namedtuple
from concurrent.futures import ThreadPoolExecutor, as_completed
import ipaddress
from itertools import islice
import json
import time
import alibabacloud_credentials as credentials
import alibabacloud_credentials.client
import alibabacloud_credentials.models
import alibabacloud_ecs20140526 as ecs
import alibabacloud_ecs20140526.client
import alibabacloud_ecs20140526.models
import alibabacloud_ram20150501 as ram
import alibabacloud_ram20150501.client
import alibabacloud_ram20150501.models
import alibabacloud_tea_openapi as openapi
import alibabacloud_tea_openapi.client
import alibabacloud_tea_openapi.models
import alibabacloud_tea_util as util
import alibabacloud_tea_util.client
import alibabacloud_tea_util.models
import alibabacloud_vpc20160428 as vpc
import alibabacloud_vpc20160428.client
import alibabacloud_vpc20160428.models
ECS_ENDPOINT = 'ecs.aliyuncs.com'
RAM_ENDPOINT = 'ram.aliyuncs.com'
IPXE_VPC_TAG = 'ipxe-default-vpc'
IPXE_VSWITCH_TAG = 'ipxe-default-vswitch'
IPXE_SG_TAG = 'ipxe-default-sg'
IPXE_CENSORSHIP_BYPASS_ROLE_NAME = 'iPXECensorshipBypassRole'
IPXE_CENSORSHIP_BYPASS_ROLE_ASSUME_POLICY = {
'Statement': [{
'Action': 'sts:AssumeRole',
'Effect': 'Allow',
'Principal': {'Service': ['ecs.aliyuncs.com']},
}],
'Version': '1',
}
Clients = namedtuple('Clients', ['region', 'ecs', 'vpc'])
def all_regions():
"""Get list of all regions"""
cred = credentials.client.Client()
conf = openapi.models.Config(credential=cred, endpoint=ECS_ENDPOINT)
client = ecs.client.Client(conf)
req = ecs.models.DescribeRegionsRequest()
rsp = client.describe_regions(req)
regions = sorted(x.region_id for x in rsp.body.regions.region)
return regions
def all_clients(region):
"""Construct all per-region clients"""
cred = credentials.client.Client()
conf = openapi.models.Config(credential=cred, region_id=region)
clients = Clients(
region=region,
ecs=ecs.client.Client(conf),
vpc=vpc.client.Client(conf),
)
return clients
def ram_client():
"""Construct resource access management client"""
cred = credentials.client.Client()
conf = openapi.models.Config(credential=cred, endpoint=RAM_ENDPOINT)
client = ram.client.Client(conf)
return client
def setup_censorship_bypass_role(client):
"""Set up censorship bypass role (required for importing images)"""
role_name = IPXE_CENSORSHIP_BYPASS_ROLE_NAME
assume_policy = json.dumps(IPXE_CENSORSHIP_BYPASS_ROLE_ASSUME_POLICY)
req = ram.models.GetRoleRequest(
role_name=role_name,
)
try:
rsp = client.get_role(req)
arn = rsp.body.role.arn
except openapi.exceptions.ClientException as exc:
if exc.code != 'EntityNotExist.Role':
raise
req = ram.models.CreateRoleRequest(
role_name=role_name,
assume_role_policy_document=assume_policy,
)
rsp = client.create_role(req)
arn = rsp.body.role.arn
req = ram.models.UpdateRoleRequest(
role_name=role_name,
new_assume_role_policy_document=assume_policy,
new_description="iPXE role to help bypass OSS censorship restrictions",
)
rsp = client.update_role(req)
req = ram.models.AttachPolicyToRoleRequest(
role_name=role_name,
policy_type='System',
policy_name='AliyunOSSFullAccess',
)
try:
rsp = client.attach_policy_to_role(req)
except openapi.exceptions.ClientException as exc:
if exc.code != 'EntityAlreadyExists.Role.Policy':
raise
return arn
def setup_vpc(clients):
"""Set up VPC"""
tag = vpc.models.DescribeVpcsRequestTag(
key=IPXE_VPC_TAG,
value=IPXE_VPC_TAG,
)
req = vpc.models.DescribeVpcsRequest(
region_id=clients.region,
tag=[tag],
)
rsp = clients.vpc.describe_vpcs(req)
vpcs = rsp.body.vpcs.vpc or []
if vpcs:
assert len(vpcs) == 1
vpc_id = vpcs[0].vpc_id
if not vpcs:
tag = vpc.models.CreateVpcRequestTag(
key=IPXE_VPC_TAG,
value=IPXE_VPC_TAG,
)
req = vpc.models.CreateVpcRequest(
region_id=clients.region,
tag=[tag],
)
rsp = clients.vpc.create_vpc(req)
vpc_id = rsp.body.vpc_id
while True:
time.sleep(1)
req = vpc.models.DescribeVpcsRequest(
region_id=clients.region,
vpc_id=vpc_id,
)
rsp = clients.vpc.describe_vpcs(req)
status = rsp.body.vpcs.vpc[0].status
if status != 'Pending':
break
if status != 'Available':
raise RuntimeError(status)
req = vpc.models.ModifyVpcAttributeRequest(
region_id=clients.region,
vpc_id=vpc_id,
vpc_name=("%s-%s" % (IPXE_VPC_TAG, clients.region)),
description="Default VPC for iPXE development and testing",
)
rsp = clients.vpc.modify_vpc_attribute(req)
req = vpc.models.ModifyVpcAttributeRequest(
region_id=clients.region,
vpc_id=vpc_id,
enable_ipv_6=True,
)
try:
rsp = clients.vpc.modify_vpc_attribute(req)
except openapi.exceptions.ClientException as exc:
# AliCloud provides no other way to detect regions without IPv6 support
if exc.code != 'OperationUnsupported.Ipv6Feature':
raise
return vpc_id
def setup_vswitch(clients, vpc_id, zone_id, index):
"""Set up vSwitch"""
tag = vpc.models.DescribeVSwitchesRequestTag(
key=IPXE_VSWITCH_TAG,
value=IPXE_VSWITCH_TAG,
)
req = vpc.models.DescribeVSwitchesRequest(
region_id=clients.region,
zone_id=zone_id,
tag=[tag],
)
rsp = clients.vpc.describe_vswitches(req)
vswitches = rsp.body.v_switches.v_switch or []
if vswitches:
assert len(vswitches) == 1
assert vswitches[0].vpc_id == vpc_id
vswitch_id = vswitches[0].v_switch_id
else:
req = vpc.models.DescribeVpcsRequest(
region_id=clients.region,
vpc_id=vpc_id,
)
rsp = clients.vpc.describe_vpcs(req)
ipv6_cidr_block = index if rsp.body.vpcs.vpc[0].enabled_ipv_6 else None
ipv4net = ipaddress.ip_network(rsp.body.vpcs.vpc[0].cidr_block)
ipv4subnet = next(islice(ipv4net.subnets(new_prefix=24), index, None))
cidr_block = str(ipv4subnet)
tag = vpc.models.CreateVSwitchRequestTag(
key=IPXE_VSWITCH_TAG,
value=IPXE_VSWITCH_TAG,
)
req = vpc.models.CreateVSwitchRequest(
region_id=clients.region,
vpc_id=vpc_id,
zone_id=zone_id,
tag=[tag],
cidr_block=cidr_block,
ipv_6cidr_block=ipv6_cidr_block,
)
try:
rsp = clients.vpc.create_vswitch(req)
vswitch_id = rsp.body.v_switch_id
except openapi.exceptions.ClientException as exc:
# AliCloud provides no other way to detect disabled zones
if exc.code != 'OperationDenied.ZoneIsDisabled':
raise
vswitch_id = None
if vswitch_id:
while True:
time.sleep(1)
req = vpc.models.DescribeVSwitchesRequest(
region_id=clients.region,
v_switch_id=vswitch_id,
)
rsp = clients.vpc.describe_vswitches(req)
status = rsp.body.v_switches.v_switch[0].status
if status != 'Pending':
break
if status != 'Available':
raise RuntimeError(status)
req = vpc.models.ModifyVSwitchAttributeRequest(
region_id=clients.region,
v_switch_id=vswitch_id,
v_switch_name=('%s-%s' % (IPXE_VSWITCH_TAG, zone_id)),
description="Default vSwitch for iPXE development and testing",
)
rsp = clients.vpc.modify_vswitch_attribute(req)
return vswitch_id
def setup_vswitches(clients, vpc_id):
"""Set up vSwitches"""
req = vpc.models.DescribeZonesRequest(region_id=clients.region)
rsp = clients.vpc.describe_zones(req)
vswitch_ids = [setup_vswitch(clients, vpc_id, zone.zone_id, index)
for index, zone in enumerate(rsp.body.zones.zone or [])]
return sorted(filter(None, vswitch_ids))
def setup_sg(clients, vpc_id):
"""Set up security group"""
tag = ecs.models.DescribeSecurityGroupsRequestTag(
key=IPXE_SG_TAG,
value=IPXE_SG_TAG,
)
req = ecs.models.DescribeSecurityGroupsRequest(
region_id=clients.region,
vpc_id=vpc_id,
tag=[tag],
)
rsp = clients.ecs.describe_security_groups(req)
sgs = rsp.body.security_groups.security_group or []
if sgs:
assert len(sgs) == 1
assert sgs[0].vpc_id == vpc_id
sg_id = sgs[0].security_group_id
else:
tag = ecs.models.CreateSecurityGroupRequestTag(
key=IPXE_SG_TAG,
value=IPXE_SG_TAG,
)
req = ecs.models.CreateSecurityGroupRequest(
region_id=clients.region,
vpc_id=vpc_id,
tag=[tag],
)
rsp = clients.ecs.create_security_group(req)
sg_id = rsp.body.security_group_id
req = ecs.models.ModifySecurityGroupAttributeRequest(
region_id=clients.region,
security_group_id=sg_id,
security_group_name=IPXE_SG_TAG,
description="Default security group for iPXE development and testing",
)
rsp = clients.ecs.modify_security_group_attribute(req)
perm4 = ecs.models.AuthorizeSecurityGroupEgressRequestPermissions(
policy='accept',
dest_cidr_ip='0.0.0.0/0',
ip_protocol='ALL',
port_range='-1/-1',
)
perm6 = ecs.models.AuthorizeSecurityGroupEgressRequestPermissions(
policy='accept',
ipv_6dest_cidr_ip='::/0',
ip_protocol='ALL',
port_range='-1/-1',
)
req = ecs.models.AuthorizeSecurityGroupEgressRequest(
region_id=clients.region,
security_group_id=sg_id,
permissions=[perm4, perm6],
)
rsp = clients.ecs.authorize_security_group_egress(req)
return sg_id
def setup_region(clients):
"""Set up region"""
vpc_id = setup_vpc(clients)
vswitch_ids = setup_vswitches(clients, vpc_id)
sg_id = setup_sg(clients, vpc_id)
return (sg_id, vpc_id, vswitch_ids)
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Set up Alibaba Cloud defaults")
parser.add_argument('--region', '-r', action='append',
help="AliCloud region(s)")
parser.add_argument('--create-role', action=argparse.BooleanOptionalAction,
default=True, help="Create censorship bypass role")
args = parser.parse_args()
# Set up censorship bypass role
if args.create_role:
arn = setup_censorship_bypass_role(ram_client())
# Use all regions if none specified
if not args.region:
args.region = all_regions()
# Construct per-region clients
clients = {region: all_clients(region) for region in args.region}
# Set up each region
with ThreadPoolExecutor(max_workers=len(args.region)) as executor:
futures = {executor.submit(setup_region,
clients=clients[region]): region
for region in args.region}
results = {futures[x]: x.result() for x in as_completed(futures)}
# Show created resources
if args.create_role:
print("%s" % arn)
for region in args.region:
(sg_id, vpc_id, vswitch_ids) = results[region]
print("%s %s %s %s" % (region, sg_id, vpc_id, " ".join(vswitch_ids)))