Support for associating and disassociating neutron floating IPs
This adds support for creating and removing DNS A records when floating IPs are associated and disassociated in neutron. novajoin-install and functional tests are enhanced to test it. Change-Id: I82c83ad9e8c84ddfd4ecfc4d5c3b31a418af97a7
This commit is contained in:
parent
7fa5789e51
commit
4d997dddc6
|
@ -511,16 +511,27 @@ class IPAClient(IPANovaJoinBase):
|
||||||
LOG.debug('IPA is not configured')
|
LOG.debug('IPA is not configured')
|
||||||
return
|
return
|
||||||
|
|
||||||
params = [{"__dns_name__": get_domain() + "."},
|
params = [six.text_type(get_domain() + '.'),
|
||||||
{"__dns_name__": hostname}]
|
six.text_type(hostname)]
|
||||||
kw = {'a_part_ip_address': floating_ip}
|
kw = {'a_part_ip_address': six.text_type(floating_ip)}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._call_ipa('dnsrecord_add', *params, **kw)
|
self._call_ipa('dnsrecord_add', *params, **kw)
|
||||||
except (errors.DuplicateEntry, errors.ValidationError):
|
except (errors.DuplicateEntry, errors.ValidationError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_ip(self, hostname, floating_ip):
|
def find_record(self, floating_ip):
|
||||||
|
"""Find DNS A record for floating IP address"""
|
||||||
|
LOG.debug('looking up host for floating ip' + floating_ip)
|
||||||
|
params = [six.text_type(get_domain() + '.')]
|
||||||
|
service_args = {'arecord': six.text_type(floating_ip)}
|
||||||
|
result = self._call_ipa('dnsrecord_find', *params, **service_args)
|
||||||
|
if result['count'] == 0:
|
||||||
|
return
|
||||||
|
assert(result['count'] == 1)
|
||||||
|
return result['result'][0]['idnsname'][0].to_unicode()
|
||||||
|
|
||||||
|
def remove_ip(self, floating_ip):
|
||||||
"""Remove a floating IP from a given hostname."""
|
"""Remove a floating IP from a given hostname."""
|
||||||
LOG.debug('In remove_ip')
|
LOG.debug('In remove_ip')
|
||||||
|
|
||||||
|
@ -528,4 +539,12 @@ class IPAClient(IPANovaJoinBase):
|
||||||
LOG.debug('IPA is not configured')
|
LOG.debug('IPA is not configured')
|
||||||
return
|
return
|
||||||
|
|
||||||
LOG.debug('Current a no-op')
|
hostname = self.find_record(floating_ip)
|
||||||
|
if not hostname:
|
||||||
|
LOG.debug('floating IP record not found')
|
||||||
|
return
|
||||||
|
|
||||||
|
params = [six.text_type(get_domain() + '.'), hostname]
|
||||||
|
service_args = {'arecord': six.text_type(floating_ip)}
|
||||||
|
|
||||||
|
self._call_ipa('dnsrecord_del', *params, **service_args)
|
||||||
|
|
|
@ -172,13 +172,7 @@ class NotificationEndpoint(object):
|
||||||
floating_ip = payload.get('floating_ip')
|
floating_ip = payload.get('floating_ip')
|
||||||
LOG.info("Disassociate floating IP %s" % floating_ip)
|
LOG.info("Disassociate floating IP %s" % floating_ip)
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
nova = novaclient()
|
ipa.remove_ip(floating_ip)
|
||||||
server = nova.servers.get(payload.get('instance_id'))
|
|
||||||
if server:
|
|
||||||
ipa.remove_ip(server.name, floating_ip)
|
|
||||||
else:
|
|
||||||
LOG.error("Could not resolve %s into a hostname",
|
|
||||||
payload.get('instance_id'))
|
|
||||||
|
|
||||||
@event_handlers('floatingip.update.end')
|
@event_handlers('floatingip.update.end')
|
||||||
def floating_ip_update(self, payload):
|
def floating_ip_update(self, payload):
|
||||||
|
@ -186,20 +180,24 @@ class NotificationEndpoint(object):
|
||||||
floatingip = payload.get('floatingip')
|
floatingip = payload.get('floatingip')
|
||||||
floating_ip = floatingip.get('floating_ip_address')
|
floating_ip = floatingip.get('floating_ip_address')
|
||||||
port_id = floatingip.get('port_id')
|
port_id = floatingip.get('port_id')
|
||||||
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
|
||||||
ipa = ipaclient()
|
ipa = ipaclient()
|
||||||
nova = novaclient()
|
if port_id:
|
||||||
neutron = neutronclient()
|
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
||||||
search_opts = {'id': port_id}
|
nova = novaclient()
|
||||||
ports = neutron.list_ports(**search_opts).get('ports')
|
neutron = neutronclient()
|
||||||
if len(ports) == 1:
|
search_opts = {'id': port_id}
|
||||||
device_id = ports[0].get('device_id')
|
ports = neutron.list_ports(**search_opts).get('ports')
|
||||||
if device_id:
|
if len(ports) == 1:
|
||||||
server = nova.servers.get(device_id)
|
device_id = ports[0].get('device_id')
|
||||||
if server:
|
if device_id:
|
||||||
ipa.add_ip(server.name, floating_ip)
|
server = nova.servers.get(device_id)
|
||||||
|
if server:
|
||||||
|
ipa.add_ip(server.name, floating_ip)
|
||||||
|
else:
|
||||||
|
LOG.error("Expected 1 port, got %d", len(ports))
|
||||||
else:
|
else:
|
||||||
LOG.error("Expected 1 port, got %d", len(ports))
|
LOG.info("Neutron floating IP disassociate: %s" % floating_ip)
|
||||||
|
ipa.remove_ip(floating_ip)
|
||||||
|
|
||||||
def delete_subhosts(self, ipa, hostname_short, metadata):
|
def delete_subhosts(self, ipa, hostname_short, metadata):
|
||||||
"""Delete subhosts and remove VIPs if possible.
|
"""Delete subhosts and remove VIPs if possible.
|
||||||
|
|
|
@ -97,10 +97,16 @@ class TestEnrollment(testtools.TestCase):
|
||||||
metadata = {"ipa_enroll": "True"})
|
metadata = {"ipa_enroll": "True"})
|
||||||
|
|
||||||
server = self.conn.compute.wait_for_server(self._server)
|
server = self.conn.compute.wait_for_server(self._server)
|
||||||
self.conn.compute.add_floating_ip_to_server(
|
|
||||||
server, self._ip.floating_ip_address)
|
|
||||||
return server
|
return server
|
||||||
|
|
||||||
|
def _associate_floating_ip(self):
|
||||||
|
self.conn.compute.add_floating_ip_to_server(
|
||||||
|
self._server, self._ip.floating_ip_address)
|
||||||
|
|
||||||
|
def _disassociate_floating_ip(self):
|
||||||
|
self.conn.compute.remove_floating_ip_from_server(
|
||||||
|
self._server, self._ip.floating_ip_address)
|
||||||
|
|
||||||
def _delete_server(self):
|
def _delete_server(self):
|
||||||
if self._server:
|
if self._server:
|
||||||
self.conn.compute.delete_server(self._server)
|
self.conn.compute.delete_server(self._server)
|
||||||
|
@ -116,8 +122,25 @@ class TestEnrollment(testtools.TestCase):
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.ipaclient.find_host(TEST_INSTANCE + EXAMPLE_DOMAIN))
|
self.ipaclient.find_host(TEST_INSTANCE + EXAMPLE_DOMAIN))
|
||||||
|
|
||||||
|
@loopingcall.RetryDecorator(50, 5, 5, (AssertionError,))
|
||||||
|
def _check_ip_record_added(self):
|
||||||
|
self.assertTrue(
|
||||||
|
self.ipaclient.find_record(self._ip.floating_ip_address))
|
||||||
|
|
||||||
|
@loopingcall.RetryDecorator(50, 5, 5, (AssertionError,))
|
||||||
|
def _check_ip_record_removed(self):
|
||||||
|
self.assertFalse(
|
||||||
|
self.ipaclient.find_record(self._ip.floating_ip_address))
|
||||||
|
|
||||||
def test_enroll_server(self):
|
def test_enroll_server(self):
|
||||||
self._create_server()
|
self._create_server()
|
||||||
|
self._associate_floating_ip()
|
||||||
self._check_ipa_client_created()
|
self._check_ipa_client_created()
|
||||||
|
self._check_ip_record_added()
|
||||||
|
self._disassociate_floating_ip()
|
||||||
|
self._check_ip_record_removed()
|
||||||
|
self._associate_floating_ip()
|
||||||
|
self._check_ip_record_added()
|
||||||
self._delete_server()
|
self._delete_server()
|
||||||
self._check_ipa_client_deleted()
|
self._check_ipa_client_deleted()
|
||||||
|
self._check_ip_record_removed()
|
||||||
|
|
|
@ -36,6 +36,10 @@
|
||||||
become: true
|
become: true
|
||||||
become_user: stack
|
become_user: stack
|
||||||
|
|
||||||
|
- name: Restart neutron services
|
||||||
|
command: systemctl restart devstack@q-*
|
||||||
|
become: true
|
||||||
|
|
||||||
- name: Restart nova services
|
- name: Restart nova services
|
||||||
command: systemctl restart devstack@n-*
|
command: systemctl restart devstack@n-*
|
||||||
become: true
|
become: true
|
||||||
|
|
|
@ -33,6 +33,7 @@ from urllib3.util import parse_url
|
||||||
IPACONF = '/etc/ipa/default.conf'
|
IPACONF = '/etc/ipa/default.conf'
|
||||||
NOVACONF = '/etc/nova/nova.conf'
|
NOVACONF = '/etc/nova/nova.conf'
|
||||||
NOVACPUCONF = '/etc/nova/nova-cpu.conf'
|
NOVACPUCONF = '/etc/nova/nova-cpu.conf'
|
||||||
|
NEUTRONCONF = '/etc/neutron/neutron.conf'
|
||||||
JOINCONF = '/etc/novajoin/join.conf'
|
JOINCONF = '/etc/novajoin/join.conf'
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,6 +203,17 @@ def install(opts):
|
||||||
with open(conf, 'w') as f:
|
with open(conf, 'w') as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
|
|
||||||
|
config = SafeConfigParser()
|
||||||
|
config.read(opts.neutron_conf)
|
||||||
|
config.set('oslo_messaging_notifications',
|
||||||
|
'driver',
|
||||||
|
'messagingv2')
|
||||||
|
config.set('oslo_messaging_notifications',
|
||||||
|
'topics',
|
||||||
|
'notifications,novajoin_notifications')
|
||||||
|
with open(opts.neutron_conf, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
|
||||||
if transport_url:
|
if transport_url:
|
||||||
join_config = SafeConfigParser()
|
join_config = SafeConfigParser()
|
||||||
|
@ -241,6 +253,9 @@ def parse_args():
|
||||||
parser.add_argument('--nova-cpu-conf', dest='nova_cpu_conf',
|
parser.add_argument('--nova-cpu-conf', dest='nova_cpu_conf',
|
||||||
help='nova compute configuration file',
|
help='nova compute configuration file',
|
||||||
default=NOVACPUCONF)
|
default=NOVACPUCONF)
|
||||||
|
parser.add_argument('--neutron-conf', dest='neutron_conf',
|
||||||
|
help='neutron configuration file',
|
||||||
|
default=NEUTRONCONF)
|
||||||
parser.add_argument('--novajoin-conf', dest='novajoin_conf',
|
parser.add_argument('--novajoin-conf', dest='novajoin_conf',
|
||||||
help='novajoin configuration file',
|
help='novajoin configuration file',
|
||||||
default=JOINCONF)
|
default=JOINCONF)
|
||||||
|
|
Loading…
Reference in New Issue