add option to use ipv6 for image update and node launching
add option to use ipv6 as ssh connect ip for building snapshot image and launching jenkins slaves. Conflicts: doc/source/configuration.rst nodepool/nodepool.py Change-Id: I7e023e7581fc0b5ec1ee34d1e5a1eeaacd7d3bfd
This commit is contained in:
parent
e8315afdfa
commit
5fe5ef8311
|
@ -243,6 +243,7 @@ provider, the Nodepool image types are also defined (see
|
||||||
template-hostname: '{image.name}-{timestamp}.template.openstack.org'
|
template-hostname: '{image.name}-{timestamp}.template.openstack.org'
|
||||||
pool: 'public'
|
pool: 'public'
|
||||||
image-type: qcow2
|
image-type: qcow2
|
||||||
|
ipv6-preferred: False
|
||||||
networks:
|
networks:
|
||||||
- net-id: 'some-uuid'
|
- net-id: 'some-uuid'
|
||||||
- net-label: 'some-network-name'
|
- net-label: 'some-network-name'
|
||||||
|
@ -348,6 +349,12 @@ provider, the Nodepool image types are also defined (see
|
||||||
queried via the Nova os-tenant-networks API extension (this requires that
|
queried via the Nova os-tenant-networks API extension (this requires that
|
||||||
the cloud provider has deployed this extension).
|
the cloud provider has deployed this extension).
|
||||||
|
|
||||||
|
``ipv6_preferred``
|
||||||
|
If it is set to True, nodepool will try to find ipv6 in public net first
|
||||||
|
as the ip address for ssh connection to build snapshot images and create
|
||||||
|
jenkins slave definition. If ipv6 is not found or the key is not
|
||||||
|
specified or set to False, ipv4 address will be used.
|
||||||
|
|
||||||
``pool``
|
``pool``
|
||||||
Specify a floating ip pool in cases where the 'public' pool is unavailable
|
Specify a floating ip pool in cases where the 'public' pool is unavailable
|
||||||
or undesirable.
|
or undesirable.
|
||||||
|
|
|
@ -83,14 +83,29 @@ class FakeList(object):
|
||||||
|
|
||||||
def create(self, **kw):
|
def create(self, **kw):
|
||||||
should_fail = kw.get('SHOULD_FAIL', '').lower() == 'true'
|
should_fail = kw.get('SHOULD_FAIL', '').lower() == 'true'
|
||||||
|
nics = kw.get('nics', [])
|
||||||
|
addresses = None
|
||||||
|
# if keyword 'ipv6-uuid' is found in provider config,
|
||||||
|
# ipv6 address will be available in public addr dict.
|
||||||
|
for nic in nics:
|
||||||
|
if 'ipv6-uuid' not in nic['net-id']:
|
||||||
|
continue
|
||||||
|
addresses = dict(
|
||||||
|
public=[dict(version=4, addr='fake'),
|
||||||
|
dict(version=6, addr='fake_v6')],
|
||||||
|
private=[dict(version=4, addr='fake')]
|
||||||
|
)
|
||||||
|
break
|
||||||
|
if not addresses:
|
||||||
|
addresses = dict(
|
||||||
|
public=[dict(version=4, addr='fake')],
|
||||||
|
private=[dict(version=4, addr='fake')]
|
||||||
|
)
|
||||||
s = Dummy(id=uuid.uuid4().hex,
|
s = Dummy(id=uuid.uuid4().hex,
|
||||||
name=kw['name'],
|
name=kw['name'],
|
||||||
status='BUILD',
|
status='BUILD',
|
||||||
adminPass='fake',
|
adminPass='fake',
|
||||||
addresses=dict(
|
addresses=addresses,
|
||||||
public=[dict(version=4, addr='fake')],
|
|
||||||
private=[dict(version=4, addr='fake')]
|
|
||||||
),
|
|
||||||
metadata=kw.get('meta', {}),
|
metadata=kw.get('meta', {}),
|
||||||
manager=self,
|
manager=self,
|
||||||
should_fail=should_fail)
|
should_fail=should_fail)
|
||||||
|
|
|
@ -478,6 +478,13 @@ class NodeLauncher(threading.Thread):
|
||||||
server['status']))
|
server['status']))
|
||||||
|
|
||||||
ip = server.get('public_v4')
|
ip = server.get('public_v4')
|
||||||
|
ip_v6 = server.get('public_v6')
|
||||||
|
if self.provider.ipv6_preferred:
|
||||||
|
if ip_v6:
|
||||||
|
ip = ip_v6
|
||||||
|
else:
|
||||||
|
self.log.warning('Preferred ipv6 not available, '
|
||||||
|
'falling back to ipv4.')
|
||||||
if not ip and self.manager.hasExtension('os-floating-ips'):
|
if not ip and self.manager.hasExtension('os-floating-ips'):
|
||||||
ip = self.manager.addPublicIP(server_id,
|
ip = self.manager.addPublicIP(server_id,
|
||||||
pool=self.provider.pool)
|
pool=self.provider.pool)
|
||||||
|
@ -486,8 +493,9 @@ class NodeLauncher(threading.Thread):
|
||||||
|
|
||||||
self.node.ip_private = server.get('private_v4')
|
self.node.ip_private = server.get('private_v4')
|
||||||
self.node.ip = ip
|
self.node.ip = ip
|
||||||
self.log.debug("Node id: %s is running, ip: %s, ipv6: %s" %
|
self.log.debug("Node id: %s is running, ipv4: %s, ipv6: %s" %
|
||||||
(self.node.id, ip, server.get('public_v6')))
|
(self.node.id, server.get('public_v4'),
|
||||||
|
server.get('public_v6')))
|
||||||
|
|
||||||
self.log.debug("Node id: %s testing ssh at ip: %s" %
|
self.log.debug("Node id: %s testing ssh at ip: %s" %
|
||||||
(self.node.id, ip))
|
(self.node.id, ip))
|
||||||
|
@ -761,6 +769,13 @@ class SubNodeLauncher(threading.Thread):
|
||||||
self.node_id, server['status']))
|
self.node_id, server['status']))
|
||||||
|
|
||||||
ip = server.get('public_v4')
|
ip = server.get('public_v4')
|
||||||
|
ip_v6 = server.get('public_v6')
|
||||||
|
if self.provider.ipv6_preferred:
|
||||||
|
if ip_v6:
|
||||||
|
ip = ip_v6
|
||||||
|
else:
|
||||||
|
self.log.warning('Preferred ipv6 not available, '
|
||||||
|
'falling back to ipv4.')
|
||||||
if not ip and self.manager.hasExtension('os-floating-ips'):
|
if not ip and self.manager.hasExtension('os-floating-ips'):
|
||||||
ip = self.manager.addPublicIP(server_id,
|
ip = self.manager.addPublicIP(server_id,
|
||||||
pool=self.provider.pool)
|
pool=self.provider.pool)
|
||||||
|
@ -770,8 +785,8 @@ class SubNodeLauncher(threading.Thread):
|
||||||
self.subnode.ip_private = server.get('private_v4')
|
self.subnode.ip_private = server.get('private_v4')
|
||||||
self.subnode.ip = ip
|
self.subnode.ip = ip
|
||||||
self.log.debug("Subnode id: %s for node id: %s is running, "
|
self.log.debug("Subnode id: %s for node id: %s is running, "
|
||||||
"ip: %s, ipv6: %s" %
|
"ipv4: %s, ipv6: %s" %
|
||||||
(self.subnode_id, self.node_id, ip,
|
(self.subnode_id, self.node_id, server.get('public_v4'),
|
||||||
server.get('public_v6')))
|
server.get('public_v6')))
|
||||||
|
|
||||||
self.log.debug("Subnode id: %s for node id: %s testing ssh at ip: %s" %
|
self.log.debug("Subnode id: %s for node id: %s testing ssh at ip: %s" %
|
||||||
|
@ -1092,12 +1107,19 @@ class SnapshotImageUpdater(ImageUpdater):
|
||||||
(server_id, self.snap_image.id, server['status']))
|
(server_id, self.snap_image.id, server['status']))
|
||||||
|
|
||||||
ip = server.get('public_v4')
|
ip = server.get('public_v4')
|
||||||
|
ip_v6 = server.get('public_v6')
|
||||||
|
if self.provider.ipv6_preferred:
|
||||||
|
if ip_v6:
|
||||||
|
ip = ip_v6
|
||||||
|
else:
|
||||||
|
self.log.warning('Preferred ipv6 not available, '
|
||||||
|
'falling back to ipv4.')
|
||||||
if not ip and self.manager.hasExtension('os-floating-ips'):
|
if not ip and self.manager.hasExtension('os-floating-ips'):
|
||||||
ip = self.manager.addPublicIP(server_id,
|
ip = self.manager.addPublicIP(server_id,
|
||||||
pool=self.provider.pool)
|
pool=self.provider.pool)
|
||||||
if not ip:
|
if not ip:
|
||||||
raise Exception("Unable to find public IP of server")
|
raise Exception("Unable to find public IP of server")
|
||||||
server['public_v4'] = ip
|
server['public_ip'] = ip
|
||||||
|
|
||||||
self.bootstrapServer(server, key, use_password=use_password)
|
self.bootstrapServer(server, key, use_password=use_password)
|
||||||
|
|
||||||
|
@ -1145,7 +1167,7 @@ class SnapshotImageUpdater(ImageUpdater):
|
||||||
else:
|
else:
|
||||||
ssh_kwargs['password'] = server['admin_pass']
|
ssh_kwargs['password'] = server['admin_pass']
|
||||||
|
|
||||||
host = utils.ssh_connect(server['public_v4'], 'root', ssh_kwargs,
|
host = utils.ssh_connect(server['public_ip'], 'root', ssh_kwargs,
|
||||||
timeout=CONNECT_TIMEOUT)
|
timeout=CONNECT_TIMEOUT)
|
||||||
|
|
||||||
if not host:
|
if not host:
|
||||||
|
@ -1154,7 +1176,7 @@ class SnapshotImageUpdater(ImageUpdater):
|
||||||
# didn't occur), we can connect with a very sort timeout.
|
# didn't occur), we can connect with a very sort timeout.
|
||||||
for username in ['ubuntu', 'fedora', 'cloud-user', 'centos']:
|
for username in ['ubuntu', 'fedora', 'cloud-user', 'centos']:
|
||||||
try:
|
try:
|
||||||
host = utils.ssh_connect(server['public_v4'], username,
|
host = utils.ssh_connect(server['public_ip'], username,
|
||||||
ssh_kwargs,
|
ssh_kwargs,
|
||||||
timeout=10)
|
timeout=10)
|
||||||
if host:
|
if host:
|
||||||
|
@ -1341,6 +1363,7 @@ class NodePool(threading.Thread):
|
||||||
p.launch_timeout = provider.get('launch-timeout', 3600)
|
p.launch_timeout = provider.get('launch-timeout', 3600)
|
||||||
p.use_neutron = bool(provider.get('networks', ()))
|
p.use_neutron = bool(provider.get('networks', ()))
|
||||||
p.networks = provider.get('networks')
|
p.networks = provider.get('networks')
|
||||||
|
p.ipv6_preferred = provider.get('ipv6-preferred')
|
||||||
p.azs = provider.get('availability-zones')
|
p.azs = provider.get('availability-zones')
|
||||||
p.template_hostname = provider.get(
|
p.template_hostname = provider.get(
|
||||||
'template-hostname',
|
'template-hostname',
|
||||||
|
@ -1479,6 +1502,7 @@ class NodePool(threading.Thread):
|
||||||
new_pm.launch_timeout != old_pm.provider.launch_timeout or
|
new_pm.launch_timeout != old_pm.provider.launch_timeout or
|
||||||
new_pm.use_neutron != old_pm.provider.use_neutron or
|
new_pm.use_neutron != old_pm.provider.use_neutron or
|
||||||
new_pm.networks != old_pm.provider.networks or
|
new_pm.networks != old_pm.provider.networks or
|
||||||
|
new_pm.ipv6_preferred != old_pm.provider.ipv6_preferred or
|
||||||
new_pm.azs != old_pm.provider.azs):
|
new_pm.azs != old_pm.provider.azs):
|
||||||
return False
|
return False
|
||||||
new_images = new_pm.images
|
new_images = new_pm.images
|
||||||
|
|
|
@ -43,7 +43,7 @@ def iterate_timeout(max_seconds, purpose):
|
||||||
|
|
||||||
|
|
||||||
def ssh_connect(ip, username, connect_kwargs={}, timeout=60):
|
def ssh_connect(ip, username, connect_kwargs={}, timeout=60):
|
||||||
if ip == 'fake':
|
if 'fake' in ip:
|
||||||
return fakeprovider.FakeSSHClient()
|
return fakeprovider.FakeSSHClient()
|
||||||
# HPcloud may return ECONNREFUSED or EHOSTUNREACH
|
# HPcloud may return ECONNREFUSED or EHOSTUNREACH
|
||||||
# for about 30 seconds after adding the IP
|
# for about 30 seconds after adding the IP
|
||||||
|
|
|
@ -102,6 +102,7 @@ class BaseTestCase(testtools.TestCase, testresources.ResourcedTestCase):
|
||||||
'fake-provider',
|
'fake-provider',
|
||||||
'fake-provider1',
|
'fake-provider1',
|
||||||
'fake-provider2',
|
'fake-provider2',
|
||||||
|
'fake-provider3',
|
||||||
'fake-dib-provider',
|
'fake-dib-provider',
|
||||||
'fake-jenkins',
|
'fake-jenkins',
|
||||||
'fake-target',
|
'fake-target',
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
script-dir: .
|
||||||
|
dburi: '{dburi}'
|
||||||
|
|
||||||
|
cron:
|
||||||
|
check: '*/15 * * * *'
|
||||||
|
cleanup: '*/1 * * * *'
|
||||||
|
image-update: '14 2 * * *'
|
||||||
|
|
||||||
|
zmq-publishers:
|
||||||
|
- tcp://localhost:8881
|
||||||
|
|
||||||
|
#gearman-servers:
|
||||||
|
# - host: localhost
|
||||||
|
|
||||||
|
labels:
|
||||||
|
- name: fake-label1
|
||||||
|
image: fake-image
|
||||||
|
min-ready: 1
|
||||||
|
providers:
|
||||||
|
- name: fake-provider1
|
||||||
|
|
||||||
|
- name: fake-label2
|
||||||
|
image: fake-image
|
||||||
|
min-ready: 1
|
||||||
|
providers:
|
||||||
|
- name: fake-provider2
|
||||||
|
|
||||||
|
- name: fake-label3
|
||||||
|
image: fake-image
|
||||||
|
min-ready: 1
|
||||||
|
providers:
|
||||||
|
- name: fake-provider3
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: fake-provider1
|
||||||
|
keypair: 'if-present-use-this-keypair'
|
||||||
|
username: 'fake'
|
||||||
|
password: 'fake'
|
||||||
|
auth-url: 'fake'
|
||||||
|
project-id: 'fake'
|
||||||
|
max-servers: 96
|
||||||
|
pool: 'fake'
|
||||||
|
networks:
|
||||||
|
- net-id: 'ipv6-uuid'
|
||||||
|
ipv6-preferred: True
|
||||||
|
rate: 0.0001
|
||||||
|
images:
|
||||||
|
- name: fake-image
|
||||||
|
base-image: 'Fake Precise'
|
||||||
|
min-ram: 8192
|
||||||
|
name-filter: 'Fake'
|
||||||
|
setup: prepare_node_devstack.sh
|
||||||
|
|
||||||
|
- name: fake-provider2
|
||||||
|
keypair: 'if-present-use-this-keypair'
|
||||||
|
username: 'fake'
|
||||||
|
password: 'fake'
|
||||||
|
auth-url: 'fake'
|
||||||
|
project-id: 'fake'
|
||||||
|
max-servers: 96
|
||||||
|
pool: 'fake'
|
||||||
|
networks:
|
||||||
|
- net-id: 'ipv6-uuid'
|
||||||
|
rate: 0.0001
|
||||||
|
images:
|
||||||
|
- name: fake-image
|
||||||
|
base-image: 'Fake Precise'
|
||||||
|
min-ram: 8192
|
||||||
|
name-filter: 'Fake'
|
||||||
|
setup: prepare_node_devstack.sh
|
||||||
|
|
||||||
|
- name: fake-provider3
|
||||||
|
keypair: 'if-present-use-this-keypair'
|
||||||
|
username: 'fake'
|
||||||
|
password: 'fake'
|
||||||
|
auth-url: 'fake'
|
||||||
|
project-id: 'fake'
|
||||||
|
max-servers: 96
|
||||||
|
pool: 'fake'
|
||||||
|
networks:
|
||||||
|
- net-id: 'some-uuid'
|
||||||
|
ipv6-preferred: True
|
||||||
|
rate: 0.0001
|
||||||
|
images:
|
||||||
|
- name: fake-image
|
||||||
|
base-image: 'Fake Precise'
|
||||||
|
min-ram: 8192
|
||||||
|
name-filter: 'Fake'
|
||||||
|
setup: prepare_node_devstack.sh
|
||||||
|
|
||||||
|
targets:
|
||||||
|
- name: fake-target
|
||||||
|
jenkins:
|
||||||
|
url: https://jenkins.example.org/
|
||||||
|
user: fake
|
||||||
|
apikey: fake
|
|
@ -207,6 +207,39 @@ class TestNodepool(tests.DBTestCase):
|
||||||
self.assertEqual(len(nodes), 1)
|
self.assertEqual(len(nodes), 1)
|
||||||
self.assertEqual(nodes[0].az, 'az1')
|
self.assertEqual(nodes[0].az, 'az1')
|
||||||
|
|
||||||
|
def test_node_ipv6(self):
|
||||||
|
"""Test that a node is created w/ or w/o ipv6 preferred flag"""
|
||||||
|
configfile = self.setup_config('node_ipv6.yaml')
|
||||||
|
pool = self.useNodepool(configfile, watermark_sleep=1)
|
||||||
|
pool.start()
|
||||||
|
self.waitForImage(pool, 'fake-provider1', 'fake-image')
|
||||||
|
self.waitForImage(pool, 'fake-provider2', 'fake-image')
|
||||||
|
self.waitForImage(pool, 'fake-provider3', 'fake-image')
|
||||||
|
self.waitForNodes(pool)
|
||||||
|
|
||||||
|
with pool.getDB().getSession() as session:
|
||||||
|
# ipv6 preferred set to true and ipv6 address available
|
||||||
|
nodes = session.getNodes(provider_name='fake-provider1',
|
||||||
|
label_name='fake-label1',
|
||||||
|
target_name='fake-target',
|
||||||
|
state=nodedb.READY)
|
||||||
|
self.assertEqual(len(nodes), 1)
|
||||||
|
self.assertEqual(nodes[0].ip, 'fake_v6')
|
||||||
|
# ipv6 preferred unspecified and ipv6 address available
|
||||||
|
nodes = session.getNodes(provider_name='fake-provider2',
|
||||||
|
label_name='fake-label2',
|
||||||
|
target_name='fake-target',
|
||||||
|
state=nodedb.READY)
|
||||||
|
self.assertEqual(len(nodes), 1)
|
||||||
|
self.assertEqual(nodes[0].ip, 'fake')
|
||||||
|
# ipv6 preferred set to true but ipv6 address unavailable
|
||||||
|
nodes = session.getNodes(provider_name='fake-provider3',
|
||||||
|
label_name='fake-label3',
|
||||||
|
target_name='fake-target',
|
||||||
|
state=nodedb.READY)
|
||||||
|
self.assertEqual(len(nodes), 1)
|
||||||
|
self.assertEqual(nodes[0].ip, 'fake')
|
||||||
|
|
||||||
def test_node_delete_success(self):
|
def test_node_delete_success(self):
|
||||||
configfile = self.setup_config('node.yaml')
|
configfile = self.setup_config('node.yaml')
|
||||||
pool = self.useNodepool(configfile, watermark_sleep=1)
|
pool = self.useNodepool(configfile, watermark_sleep=1)
|
||||||
|
|
Loading…
Reference in New Issue