Skip to content

Instantly share code, notes, and snippets.

@dmsimard
Created May 4, 2018 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmsimard/6b21c9818878f8aacaf3ddaf63c669b3 to your computer and use it in GitHub Desktop.
Save dmsimard/6b21c9818878f8aacaf3ddaf63c669b3 to your computer and use it in GitHub Desktop.
#!/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