Created
May 4, 2018 15:56
-
-
Save dmsimard/6b21c9818878f8aacaf3ddaf63c669b3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Copyright Red Hat, Inc. All Rights Reserved. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); you may | |
# not use this file except in compliance with the License. You may obtain | |
# a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
# License for the specific language governing permissions and limitations | |
# under the License. | |
# | |
# Adds IPs from a subnet as allowed address pairs to another subnet | |
import argparse | |
import logging | |
import logging.config | |
import json | |
from netaddr import iter_iprange | |
import os | |
import shade | |
import yaml | |
def setup_logging(level): | |
log_config = """ | |
--- | |
version: 1 | |
formatters: | |
console: | |
format: '%(asctime)s %(levelname)s %(name)s: %(message)s' | |
handlers: | |
console: | |
class: logging.StreamHandler | |
formatter: console | |
level: {level} | |
stream: ext://sys.stdout | |
loggers: | |
{name}: | |
handlers: | |
- console | |
level: {level} | |
propagate: 0 | |
root: | |
handlers: | |
- console | |
level: {level} | |
""".format(name=os.path.basename(__file__), level=level).lstrip() | |
logging.config.dictConfig(yaml.safe_load(log_config)) | |
def get_args(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--debug', help='Enable debug logging', | |
action='store_true') | |
parser.add_argument('cloud', help='Name of the cloud in clouds.yml') | |
parser.add_argument('source', help='Subnet CIDR to add as allowed.' | |
' Ex: 172.16.0.0/24') | |
parser.add_argument('destination', help='Ports matching destination CIDR' | |
' will be updated.') | |
args = parser.parse_args() | |
return args | |
def crop_pairs(pairs, limit=10): | |
while len(pairs) >= limit: | |
pairs.pop() | |
return pairs | |
def main(): | |
args = get_args() | |
level = "DEBUG" if args.debug else "INFO" | |
setup_logging(level) | |
log = logging.getLogger(os.path.basename(__file__)) | |
log.debug("Arguments: %s" % json.dumps(args.__dict__)) | |
cloud = shade.openstack_cloud(cloud=args.cloud) | |
source_subnet = cloud.search_subnets(filters=dict( | |
cidr=args.source | |
)) | |
if not source_subnet: | |
log.fatal('Could not find source subnet %s' % args.source) | |
else: | |
source_subnet = source_subnet[0] | |
destination_subnet = cloud.search_subnets(filters=dict( | |
cidr=args.destination | |
)) | |
if not destination_subnet: | |
log.fatal('Could not find destination subnet %s' % args.destination) | |
else: | |
destination_subnet = destination_subnet[0] | |
# Can't use search_ports, fixed_ips is a nested list of dicts | |
# https://github.com/openstack-infra/shade/blob/16e7290ff45ca6fa5d7ba7360575c38e0b0fd043/shade/openstackcloud.py#L6237-L6247 | |
ports = cloud.list_ports() | |
# Ensure ports have names based on their IPs to allow easy filtering | |
log.info('Making sure all ports have names') | |
for port in ports: | |
if port['fixed_ips'] and not port['name']: | |
if len(port['fixed_ips']) > 1: | |
log.fatal('%s has more than one fixed ip' % port['id']) | |
address = port['fixed_ips'][0]['ip_address'] | |
log.info('Adding name to port for %s' % address) | |
cloud.update_port( | |
port['id'], | |
name=address, | |
) | |
# Ensure all addresses from the allocation pools have ports created. | |
# These ports are never really going to be attached, we just need to create | |
# a port first so we can add the IP address of the port as an | |
# allowed-address-pair. | |
source_ports = [] | |
log.info('Checking if any ports in %s must be created' % args.source) | |
for pool in source_subnet['allocation_pools']: | |
addresses = list(iter_iprange(pool['start'], pool['end'])) | |
for address in addresses: | |
port = cloud.get_port(address) | |
if not port: | |
log.info('Creating port for %s' % address) | |
cloud.create_port( | |
network_id=source_subnet['network_id'], | |
name=address, | |
fixed_ips=[dict( | |
ip_address=address, | |
subnet_id=source_subnet['id'] | |
)] | |
) | |
else: | |
if len(port['fixed_ips']) > 1: | |
log.fatal('%s has more than one fixed ip' % port['id']) | |
source_ports.append(port) | |
log.info('Checking if any ports in %s must be updated' % args.destination) | |
for pool in destination_subnet['allocation_pools']: | |
addresses = list(iter_iprange(pool['start'], pool['end'])) | |
for address in addresses: | |
port = cloud.get_port(address) | |
# We might not get a result back, that's okay. | |
# It just means that there hasn't been a port created/attached to | |
# a VM yet for that address. | |
if not port: | |
log.debug('No destination port found for %s' % address) | |
continue | |
if len(port['fixed_ips']) > 1: | |
log.fatal('%s has more than one fixed ip' % port['id']) | |
# We need to add the port's own IP at the very least | |
address_pairs = [dict( | |
ip_address=port['fixed_ips'][0]['ip_address'], | |
mac_address=port['mac_address'] | |
)] | |
# Set up allowed address pairs | |
for source_port in source_ports: | |
if len(port['fixed_ips']) > 1: | |
log.fatal('%s has more than one fixed ip' % port['id']) | |
address_pairs.append(dict( | |
ip_address=source_port['fixed_ips'][0]['ip_address'], | |
mac_address=source_port['mac_address'] | |
)) | |
# Update port to contain all pairs | |
log.info('Setting %s address pairs for %s' % ( | |
len(address_pairs), port['id'] | |
)) | |
# Temporary quota workaround | |
if len(address_pairs) > 10: | |
cloud.update_port(port['id'], | |
allowed_address_pairs=crop_pairs(address_pairs)) | |
else: | |
cloud.update_port(port['id'], | |
allowed_address_pairs=address_pairs) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment