DHCP server IP address collision with VM's IP

When a stack is imported in Heat, the DHCP server IP is set
to the lowest free IP address of it's pool.
Depending on the VM creation order, the DHCP address can
either collide with vm1's or vm2's IP.
with the alter-allocation-pools flag the exported
allocation pools starts at the DHCP's IP address.

Change-Id: I8a4815e6c8b2bafa539cf3ad8384be76ef075edc
This commit is contained in:
zarrouk 2016-08-23 08:05:20 +02:00
parent 5d7271dd98
commit 532745e610
6 changed files with 72 additions and 6 deletions

View File

@ -36,7 +36,7 @@ Usage
[--project PROJECT] [--region REGION] [--auth_url AUTH_URL]
[--insecure] [--endpoint_type ENDPOINT_TYPE] [--exclude-servers]
[--exclude-volumes] [--exclude-keypairs] [--generate-stack-data]
[--extract-ports]
[--extract-ports] [--alter-allocation-pools]
Heat template and data file generator
@ -63,6 +63,9 @@ Usage
In addition to template, generate Heat stack data
file.
--extract-ports Export the tenant network ports
--alter-allocation-pools
Have the DHCP allocation pools start at the DHCP's IP
address for the current subnet.
Usage example

View File

@ -15,4 +15,6 @@ When this stack is imported in Heat, the DHCP server IP is set to the lowest
free IP address of it's pool. Depending on the VM creation order, the DHCP
address can either collide with vm1's or vm2's IP.
Flame comes with an --alter-allocation-pools options that makes the allocation
pool of a subnet start at the original DHCP's IP address.

View File

@ -13,7 +13,7 @@ To use the CLI of flame::
[--project PROJECT] [--region REGION] [--auth_url AUTH_URL]
[--insecure] [--endpoint_type ENDPOINT_TYPE] [--exclude-servers]
[--exclude-volumes] [--exclude-keypairs] [--generate-stack-data]
[--extract-ports]
[--extract-ports] [--alter-allocation-pools]
Heat template and data file generator
@ -37,6 +37,9 @@ To use the CLI of flame::
In addition to template, generate Heat stack data
file.
--extract-ports Export the tenant network ports
--alter-allocation-pools
Have the DHCP allocation pools start at the DHCP's IP
address for the current subnet.
Example

View File

@ -75,8 +75,16 @@ def main(args=None):
parser.add_argument('--extract-ports', action='store_true',
default=False,
help="Export the tenant network ports")
parser.add_argument('--alter-allocation-pools', action='store_true',
default=False,
help="Have the DHCP allocation pools start at the "
"DHCP's IP address for the current subnet.")
args = parser.parse_args()
if args.alter_allocation_pools and not args.extract_ports:
raise argparse.ArgumentError(None,
"Must use --extract-ports with "
"--alter-allocation-pools.")
flame = client.Client(args.username, args.password,
args.project, args.auth_url,
args.os_auth_token,
@ -88,7 +96,8 @@ def main(args=None):
args.exclude_volumes,
args.exclude_keypairs,
args.generate_stack_data,
args.extract_ports)
args.extract_ports,
args.alter_allocation_pools)
template.extract_data()
print("### Heat Template ###")
print(template.heat_template())

View File

@ -143,12 +143,14 @@ class TemplateGenerator(object):
def extract_vm_details(self, exclude_servers, exclude_volumes,
exclude_keypairs, generate_data,
extract_ports=False):
extract_ports=False,
alter_allocation_pools=False):
self.exclude_servers = exclude_servers
self.exclude_volumes = exclude_volumes
self.exclude_keypairs = exclude_keypairs
self.generate_data = generate_data
self.extract_ports = extract_ports
self.alter_allocation_pools = alter_allocation_pools
self.subnets = self.build_data(self.neutron.subnet_list())
self.networks = self.build_data(self.neutron.network_list())
@ -690,6 +692,19 @@ class TemplateGenerator(object):
resources += self._extract_ports()
subnets = self._extract_subnets()
if self.alter_allocation_pools:
for subnet in subnets:
pools = subnet.properties['allocation_pools']
fixed_ips = self.dhcp_fixed_ips.get(subnet.name, [])
for pool in pools:
for ip in fixed_ips:
start_n = socket.inet_aton(pool['start'])
end_n = socket.inet_aton(pool['end'])
ip_n = socket.inet_aton(ip)
if start_n < ip_n <= end_n:
# The DHCP IP address is in the pool, we make the
# pool start at it's IP address
pool['start'] = ip
resources += subnets
resources += self._extract_secgroups()

View File

@ -394,12 +394,14 @@ class BaseTestCase(base.TestCase):
super(BaseTestCase, self).tearDown()
def get_generator(self, exclude_servers, exclude_volumes,
exclude_keypairs, generate_data, extract_ports):
exclude_keypairs, generate_data, extract_ports,
alter_dhcp_allocation_pools=False):
generator = flame.TemplateGenerator('x', 'x', 'x', 'x', True,
'publicURL')
generator.extract_vm_details(exclude_servers, exclude_volumes,
exclude_keypairs, generate_data,
extract_ports)
extract_ports,
alter_dhcp_allocation_pools)
return generator
def check_stackdata(self, resources, expected_resources):
@ -658,3 +660,35 @@ class GenerationTests(BaseTestCase):
'status': 'COMPLETE',
'type': 'OS::Neutron::Port'}
self.assertEqual(reference, association_data)
def test_dhcp_pool_alteration(self):
generator = self.get_generator(False, False, False, True, True, True)
generator.extract_data()
subnets = [res['properties']
for res in generator.template['resources'].values()
if res['type'] == "OS::Neutron::Subnet"]
pools = {sn['name']: sn['allocation_pools'] for sn in subnets}
reference = {u'int-a-1': [{u'end': u'192.168.203.254',
u'start': u'192.168.203.3'}],
u'int-a-2': [{u'end': u'192.168.204.254',
u'start': u'192.168.204.2'}],
u'storage': [{u'end': u'172.19.0.254',
u'start': u'172.19.0.2'}]}
self.assertEqual(reference, pools)
def test_no_dhcp_pool_alteration(self):
generator = self.get_generator(False, False, False, True, True, False)
generator.extract_data()
subnets = [res['properties']
for res in generator.template['resources'].values()
if res['type'] == "OS::Neutron::Subnet"]
pools = {sn['name']: sn['allocation_pools'] for sn in subnets}
reference = {u'int-a-1': [{u'end': u'192.168.203.254',
u'start': u'192.168.203.2'}],
u'int-a-2': [{u'end': u'192.168.204.254',
u'start': u'192.168.204.2'}],
u'storage': [{u'end': u'172.19.0.254',
u'start': u'172.19.0.2'}]}
self.assertEqual(reference, pools)