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')
|
||||
return
|
||||
|
||||
params = [{"__dns_name__": get_domain() + "."},
|
||||
{"__dns_name__": hostname}]
|
||||
kw = {'a_part_ip_address': floating_ip}
|
||||
params = [six.text_type(get_domain() + '.'),
|
||||
six.text_type(hostname)]
|
||||
kw = {'a_part_ip_address': six.text_type(floating_ip)}
|
||||
|
||||
try:
|
||||
self._call_ipa('dnsrecord_add', *params, **kw)
|
||||
except (errors.DuplicateEntry, errors.ValidationError):
|
||||
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."""
|
||||
LOG.debug('In remove_ip')
|
||||
|
||||
|
@ -528,4 +539,12 @@ class IPAClient(IPANovaJoinBase):
|
|||
LOG.debug('IPA is not configured')
|
||||
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')
|
||||
LOG.info("Disassociate floating IP %s" % floating_ip)
|
||||
ipa = ipaclient()
|
||||
nova = novaclient()
|
||||
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'))
|
||||
ipa.remove_ip(floating_ip)
|
||||
|
||||
@event_handlers('floatingip.update.end')
|
||||
def floating_ip_update(self, payload):
|
||||
|
@ -186,20 +180,24 @@ class NotificationEndpoint(object):
|
|||
floatingip = payload.get('floatingip')
|
||||
floating_ip = floatingip.get('floating_ip_address')
|
||||
port_id = floatingip.get('port_id')
|
||||
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
||||
ipa = ipaclient()
|
||||
nova = novaclient()
|
||||
neutron = neutronclient()
|
||||
search_opts = {'id': port_id}
|
||||
ports = neutron.list_ports(**search_opts).get('ports')
|
||||
if len(ports) == 1:
|
||||
device_id = ports[0].get('device_id')
|
||||
if device_id:
|
||||
server = nova.servers.get(device_id)
|
||||
if server:
|
||||
ipa.add_ip(server.name, floating_ip)
|
||||
if port_id:
|
||||
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
||||
nova = novaclient()
|
||||
neutron = neutronclient()
|
||||
search_opts = {'id': port_id}
|
||||
ports = neutron.list_ports(**search_opts).get('ports')
|
||||
if len(ports) == 1:
|
||||
device_id = ports[0].get('device_id')
|
||||
if device_id:
|
||||
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:
|
||||
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):
|
||||
"""Delete subhosts and remove VIPs if possible.
|
||||
|
|
|
@ -97,10 +97,16 @@ class TestEnrollment(testtools.TestCase):
|
|||
metadata = {"ipa_enroll": "True"})
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
if self._server:
|
||||
self.conn.compute.delete_server(self._server)
|
||||
|
@ -116,8 +122,25 @@ class TestEnrollment(testtools.TestCase):
|
|||
self.assertFalse(
|
||||
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):
|
||||
self._create_server()
|
||||
self._associate_floating_ip()
|
||||
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._check_ipa_client_deleted()
|
||||
self._check_ip_record_removed()
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
become: true
|
||||
become_user: stack
|
||||
|
||||
- name: Restart neutron services
|
||||
command: systemctl restart devstack@q-*
|
||||
become: true
|
||||
|
||||
- name: Restart nova services
|
||||
command: systemctl restart devstack@n-*
|
||||
become: true
|
||||
|
|
|
@ -33,6 +33,7 @@ from urllib3.util import parse_url
|
|||
IPACONF = '/etc/ipa/default.conf'
|
||||
NOVACONF = '/etc/nova/nova.conf'
|
||||
NOVACPUCONF = '/etc/nova/nova-cpu.conf'
|
||||
NEUTRONCONF = '/etc/neutron/neutron.conf'
|
||||
JOINCONF = '/etc/novajoin/join.conf'
|
||||
|
||||
|
||||
|
@ -202,6 +203,17 @@ def install(opts):
|
|||
with open(conf, 'w') as 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:
|
||||
join_config = SafeConfigParser()
|
||||
|
@ -241,6 +253,9 @@ def parse_args():
|
|||
parser.add_argument('--nova-cpu-conf', dest='nova_cpu_conf',
|
||||
help='nova compute configuration file',
|
||||
default=NOVACPUCONF)
|
||||
parser.add_argument('--neutron-conf', dest='neutron_conf',
|
||||
help='neutron configuration file',
|
||||
default=NEUTRONCONF)
|
||||
parser.add_argument('--novajoin-conf', dest='novajoin_conf',
|
||||
help='novajoin configuration file',
|
||||
default=JOINCONF)
|
||||
|
|
Loading…
Reference in New Issue