You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
10 KiB
273 lines
10 KiB
#!/usr/bin/python |
|
|
|
import json |
|
import netaddr |
|
import os |
|
import os_client_config |
|
import subprocess |
|
|
|
CTLPLANE_NETWORK_NAME = 'ctlplane' |
|
|
|
AUTH_URL = os.environ['auth_url'] |
|
ADMIN_PASSWORD = os.environ['admin_password'] |
|
CONF = json.loads(os.environ['config']) |
|
|
|
|
|
def _run_command(args, env=None, name=None): |
|
"""Run the command defined by args and return its output |
|
|
|
:param args: List of arguments for the command to be run. |
|
:param env: Dict defining the environment variables. Pass None to use |
|
the current environment. |
|
:param name: User-friendly name for the command being run. A value of |
|
None will cause args[0] to be used. |
|
""" |
|
if name is None: |
|
name = args[0] |
|
|
|
if env is None: |
|
env = os.environ |
|
env = env.copy() |
|
|
|
# When running a localized python script, we need to tell it that we're |
|
# using utf-8 for stdout, otherwise it can't tell because of the pipe. |
|
env['PYTHONIOENCODING'] = 'utf8' |
|
|
|
try: |
|
return subprocess.check_output(args, |
|
stderr=subprocess.STDOUT, |
|
env=env).decode('utf-8') |
|
except subprocess.CalledProcessError as ex: |
|
print('ERROR: %s failed: %s' % (name, ex.output)) |
|
raise |
|
|
|
|
|
def _ensure_neutron_network(sdk): |
|
try: |
|
network = list(sdk.network.networks(name=CTLPLANE_NETWORK_NAME)) |
|
if not network: |
|
network = sdk.network.create_network( |
|
name=CTLPLANE_NETWORK_NAME, |
|
provider_network_type='flat', |
|
provider_physical_network=CONF['physical_network'], |
|
mtu=CONF['mtu']) |
|
print('INFO: Network created %s' % network) |
|
# (hjensas) Delete the default segment, we create a new segment |
|
# per subnet later. |
|
segments = list(sdk.network.segments(network_id=network.id)) |
|
sdk.network.delete_segment(segments[0].id) |
|
print('INFO: Default segment on network %s deleted.' % |
|
network.name) |
|
else: |
|
network = sdk.network.update_network( |
|
network[0].id, |
|
name=CTLPLANE_NETWORK_NAME, |
|
mtu=CONF['mtu']) |
|
print('INFO: Network updated %s' % network) |
|
except Exception: |
|
print('ERROR: Network create/update failed.') |
|
raise |
|
|
|
return network |
|
|
|
|
|
def _neutron_subnet_create(sdk, network_id, cidr, gateway, host_routes, |
|
allocation_pool, name, segment_id, dns_nameservers): |
|
try: |
|
if netaddr.IPNetwork(cidr).version == 6: |
|
subnet = sdk.network.create_subnet( |
|
name=name, |
|
cidr=cidr, |
|
gateway_ip=gateway, |
|
enable_dhcp=True, |
|
ip_version='6', |
|
ipv6_address_mode='dhcpv6-stateless', |
|
ipv6_ra_mode='dhcpv6-stateless', |
|
allocation_pools=allocation_pool, |
|
network_id=network_id, |
|
segment_id=segment_id, |
|
dns_nameservers=dns_nameservers) |
|
else: |
|
subnet = sdk.network.create_subnet( |
|
name=name, |
|
cidr=cidr, |
|
gateway_ip=gateway, |
|
host_routes=host_routes, |
|
enable_dhcp=True, |
|
ip_version='4', |
|
allocation_pools=allocation_pool, |
|
network_id=network_id, |
|
segment_id=segment_id, |
|
dns_nameservers=dns_nameservers) |
|
print('INFO: Subnet created %s' % subnet) |
|
except Exception: |
|
print('ERROR: Create subnet %s failed.' % name) |
|
raise |
|
|
|
return subnet |
|
|
|
|
|
def _neutron_subnet_update(sdk, subnet_id, cidr, gateway, host_routes, |
|
allocation_pool, name, dns_nameservers): |
|
try: |
|
if netaddr.IPNetwork(cidr).version == 6: |
|
subnet = sdk.network.update_subnet( |
|
subnet_id, |
|
name=name, |
|
gateway_ip=gateway, |
|
allocation_pools=allocation_pool, |
|
dns_nameservers=dns_nameservers) |
|
else: |
|
subnet = sdk.network.update_subnet( |
|
subnet_id, |
|
name=name, |
|
gateway_ip=gateway, |
|
host_routes=host_routes, |
|
allocation_pools=allocation_pool, |
|
dns_nameservers=dns_nameservers) |
|
print('INFO: Subnet updated %s' % subnet) |
|
except Exception: |
|
print('ERROR: Update of subnet %s failed.' % name) |
|
raise |
|
|
|
|
|
def _neutron_segment_create(sdk, name, network_id, phynet): |
|
try: |
|
segment = sdk.network.create_segment( |
|
name=name, |
|
network_id=network_id, |
|
physical_network=phynet, |
|
network_type='flat') |
|
print('INFO: Neutron Segment created %s' % segment) |
|
except Exception as ex: |
|
print('ERROR: Neutron Segment %s create failed.' % name) |
|
raise |
|
|
|
return segment |
|
|
|
|
|
def _neutron_segment_update(sdk, segment_id, name): |
|
try: |
|
segment = sdk.network.update_segment(segment_id, name=name) |
|
print('INFO: Neutron Segment updated %s', segment) |
|
except Exception: |
|
print('ERROR: Neutron Segment %s update failed.' % name) |
|
raise |
|
|
|
|
|
def _ensure_neutron_router(sdk, name, subnet_id): |
|
try: |
|
router = sdk.network.create_router(name=name, admin_state_up='true') |
|
sdk.network.add_interface_to_router(router.id, subnet_id=subnet_id) |
|
except Exception: |
|
print('ERROR: Create router for subnet %s failed.' % name) |
|
raise |
|
|
|
|
|
def _get_subnet(sdk, cidr, network_id): |
|
try: |
|
subnet = list(sdk.network.subnets(cidr=cidr, network_id=network_id)) |
|
except Exception as ex: |
|
print('ERROR: Get subnet with cidr %s failed.' % cidr) |
|
raise |
|
|
|
return False if not subnet else subnet[0] |
|
|
|
|
|
def _get_segment(sdk, phy, network_id): |
|
try: |
|
segment = list(sdk.network.segments(physical_network=phy, |
|
network_id=network_id)) |
|
except Exception: |
|
print('ERROR: Get segment for physical_network %s on network_id %s ' |
|
'failed.' % (phy, network_id)) |
|
raise |
|
|
|
return False if not segment else segment[0] |
|
|
|
|
|
def config_neutron_segments_and_subnets(sdk, ctlplane_id): |
|
s = CONF['subnets'][CONF['local_subnet']] |
|
subnet = _get_subnet(sdk, s['NetworkCidr'], ctlplane_id) |
|
if subnet and not subnet.segment_id: |
|
print('WARNING: Local subnet %s already exists and is not associated ' |
|
'with a network segment. Any additional subnets will be ' |
|
'ignored.' % CONF['local_subnet']) |
|
host_routes = [{'destination': '169.254.169.254/32', |
|
'nexthop': CONF['local_ip']}] |
|
allocation_pool = [{'start': s['DhcpRangeStart'], |
|
'end': s['DhcpRangeEnd']}] |
|
_neutron_subnet_update( |
|
sdk, subnet.id, s['NetworkCidr'], s['NetworkGateway'], host_routes, |
|
allocation_pool, CONF['local_subnet'], CONF['nameservers']) |
|
# If the subnet is IPv6 we need to start a router so that router |
|
# advertisments are sent out for stateless IP addressing to work. |
|
if netaddr.IPNetwork(s['NetworkCidr']).version == 6: |
|
_ensure_neutron_router(sdk, CONF['local_subnet'], subnet.id) |
|
else: |
|
for name in CONF['subnets']: |
|
s = CONF['subnets'][name] |
|
|
|
phynet = name |
|
metadata_nexthop = s['NetworkGateway'] |
|
if name == CONF['local_subnet']: |
|
phynet = CONF['physical_network'] |
|
metadata_nexthop = CONF['local_ip'] |
|
|
|
host_routes = [{'destination': '169.254.169.254/32', |
|
'nexthop': metadata_nexthop}] |
|
allocation_pool = [{'start': s['DhcpRangeStart'], |
|
'end': s['DhcpRangeEnd']}] |
|
|
|
subnet = _get_subnet(sdk, s['NetworkCidr'], ctlplane_id) |
|
segment = _get_segment(sdk, phynet, ctlplane_id) |
|
|
|
if name == CONF['local_subnet']: |
|
if ((subnet and not segment) or |
|
(subnet and segment and |
|
subnet.segment_id != segment.id)): |
|
raise RuntimeError( |
|
'The cidr: %s of the local subnet is already used in ' |
|
'subnet: %s which is associated with segment_id: %s.' % |
|
(s['NetworkCidr'], subnet.id, subnet.segment_id)) |
|
|
|
if subnet: |
|
_neutron_segment_update(sdk, subnet.segment_id, name) |
|
_neutron_subnet_update( |
|
sdk, subnet.id, s['NetworkCidr'], s['NetworkGateway'], |
|
host_routes, allocation_pool, name, CONF['nameservers']) |
|
else: |
|
if segment: |
|
_neutron_segment_update(sdk, segment.id, name) |
|
else: |
|
segment = _neutron_segment_create(sdk, name, |
|
ctlplane_id, phynet) |
|
|
|
if CONF['enable_routed_networks']: |
|
subnet = _neutron_subnet_create( |
|
sdk, ctlplane_id, s['NetworkCidr'], |
|
s['NetworkGateway'], host_routes, allocation_pool, |
|
name, segment.id, CONF['nameservers']) |
|
else: |
|
subnet = _neutron_subnet_create( |
|
sdk, ctlplane_id, s['NetworkCidr'], |
|
s['NetworkGateway'], host_routes, allocation_pool, |
|
name, None, CONF['nameservers']) |
|
|
|
# If the subnet is IPv6 we need to start a router so that router |
|
# advertisments are sent out for stateless IP addressing to work. |
|
if netaddr.IPNetwork(s['NetworkCidr']).version == 6: |
|
_ensure_neutron_router(sdk, name, subnet.id) |
|
|
|
|
|
|
|
if _run_command(['hiera', 'neutron_api_enabled'], name='hiera'): |
|
sdk = os_client_config.make_sdk(auth_url=AUTH_URL, |
|
project_name='admin', |
|
username='admin', |
|
password=ADMIN_PASSWORD, |
|
project_domain_name='Default', |
|
user_domain_name='Default') |
|
|
|
network = _ensure_neutron_network(sdk) |
|
config_neutron_segments_and_subnets(sdk, network.id)
|
|
|