0ba5358865
* Add a connection-string based workflow to MicroStack; * microstack add-compute command can be run at the Control node in order to generate a connection string (an ASCII blob for the user); * the connection string contains: * an address of the control node; * a sha256 fingerprint of the TLS certificate used by the clustering service at the control node (which is used during verification similar to the Certificate Pinning approach); * an application credential id; * an application credential secret (short expiration time, reader role on the service project, restricted to listing the service catalog); * a MicroStack admin is expected to have ssh access to all nodes that will participate in a cluster - prior trust establishment is on them to figure out which is normal since they provision the nodes; * a MicroStack admin is expected to securely copy a connection string to a compute node via ssh. Since it is short-lived and does not carry service secrets, there is no risk of a replay at a later time; * If the compute role is specified during microstack.init, a connection string is requested and used to perform a request to the clustering service and validate the certificate fingerprint. The credential ID and secret are POSTed for verification to the clustering service which responds with the necessary config data for the compute node upon successful authorization. * Set up TLS termination for the clustering service; * run the flask app as a UWSGI daemon behind nginx; * configure nginx to use a TLS certificate; * generate a self-signed TLS certificate. This setup does not require PKI to be present for its own purposes of joining compute nodes to the cluster. However, this does not mean that PKI will not be used for TLS termination of the OpenStack endpoints. Control node init workflow (non-interactive): sudo microstack init --auto --control microstack add-compute <the connection string to be used at the compute node> Compute node init workflow (non-interactive): sudo microstack init --auto --compute --join <connection-string> Change-Id: I9596fe1e6e5c1a325cc71fd3bf0c78b660b9a83e
129 lines
4.4 KiB
Python
Executable File
129 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python
|
|
"""
|
|
basic_test.py
|
|
|
|
This is a basic test of microstack functionality. We verify that:
|
|
|
|
1) We can install the snap.
|
|
2) We can launch a cirros image.
|
|
3) Horizon is running, and we can hit the landing page.
|
|
4) We can login to Horizon successfully.
|
|
|
|
The Horizon testing bits were are based on code generated by the Selinum
|
|
Web IDE.
|
|
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import json
|
|
import unittest
|
|
|
|
sys.path.append(os.getcwd())
|
|
|
|
from tests.framework import Framework, check, check_output, call # noqa E402
|
|
|
|
|
|
class TestBasics(Framework):
|
|
|
|
def test_basics(self):
|
|
"""Basic test
|
|
|
|
Install microstack, and verify that we can launch a machine and
|
|
open the Horizon GUI.
|
|
|
|
"""
|
|
host = self.get_host()
|
|
host.install()
|
|
host.init([
|
|
'--auto',
|
|
'--control',
|
|
'--setup-loop-based-cinder-lvm-backend',
|
|
'--loop-device-file-size=24'
|
|
])
|
|
prefix = host.prefix
|
|
|
|
endpoints = check_output(
|
|
*prefix, '/snap/bin/microstack.openstack', 'endpoint', 'list')
|
|
|
|
control_ip = check_output(
|
|
*prefix, 'sudo', 'snap', 'get',
|
|
'microstack', 'config.network.control-ip'
|
|
)
|
|
# Endpoints should contain the control IP.
|
|
self.assertTrue(control_ip in endpoints)
|
|
|
|
# Endpoints should not contain localhost
|
|
self.assertFalse("localhost" in endpoints)
|
|
|
|
# We should be able to launch an instance
|
|
print("Testing microstack.launch ...")
|
|
check(*prefix, '/snap/bin/microstack.launch', 'cirros',
|
|
'--name', 'breakfast', '--retry')
|
|
|
|
# ... and ping it
|
|
# Skip these tests in the gate, as they are not reliable there.
|
|
# TODO: fix these in the gate!
|
|
if 'multipass' in prefix:
|
|
self.verify_instance_networking(host, 'breakfast')
|
|
else:
|
|
# Artificial wait, to allow for stuff to settle for the GUI test.
|
|
# TODO: get rid of this, when we drop the ping tests back int.
|
|
time.sleep(10)
|
|
|
|
# The Horizon Dashboard should function
|
|
self.verify_gui(host)
|
|
|
|
# Verify that we can uninstall the snap cleanly, and that the
|
|
# ovs bridge goes away.
|
|
|
|
# Check to verify that our bridge is there.
|
|
self.assertTrue('br-ex' in check_output(*prefix, 'ip', 'a'))
|
|
|
|
check(*prefix, 'sudo', 'mkdir', '-p', '/tmp/snap.microstack-test/tmp')
|
|
check(*prefix, 'sudo', 'cp',
|
|
'/var/snap/microstack/common/etc/microstack.json',
|
|
'/tmp/snap.microstack-test/tmp/microstack.json')
|
|
check(*prefix, 'microstack-test.rally', 'db', 'recreate')
|
|
check(*prefix, 'microstack-test.rally', 'deployment', 'create',
|
|
'--filename', '/tmp/microstack.json',
|
|
'--name', 'snap_generated')
|
|
check(*prefix, 'microstack-test.tempest-init')
|
|
check(*prefix, 'microstack-test.rally', 'verify', 'start',
|
|
'--load-list',
|
|
'/snap/microstack-test/current/2020.06-test-list.txt',
|
|
'--detailed', '--concurrency', '2')
|
|
check(*prefix, 'microstack-test.rally', 'verify', 'report',
|
|
'--type', 'json', '--to',
|
|
'/tmp/verification-report.json')
|
|
report = json.loads(check_output(
|
|
*prefix, 'sudo', 'cat',
|
|
'/tmp/snap.microstack-test/tmp/verification-report.json'))
|
|
# Make sure there are no verification failures in the report.
|
|
failures = list(report['verifications'].values())[0]['failures']
|
|
self.assertEqual(failures, 0, 'Verification tests had failure.')
|
|
|
|
# Try to remove the snap without sudo.
|
|
self.assertFalse(
|
|
call(*prefix, 'snap', 'remove', '--purge', 'microstack'))
|
|
|
|
# Retry with sudo (should succeed).
|
|
check(*prefix, 'sudo', 'snap', 'remove', '--purge', 'microstack')
|
|
|
|
# Verify that MicroStack is gone.
|
|
self.assertFalse(call(*prefix, 'snap', 'list', 'microstack'))
|
|
|
|
# Verify that bridge is gone.
|
|
self.assertFalse('br-ex' in check_output(*prefix, 'ip', 'a'))
|
|
|
|
# We made it to the end. Set passed to True!
|
|
self.passed = True
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Run our tests, ignoring deprecation warnings and warnings about
|
|
# unclosed sockets. (TODO: setup a selenium server so that we can
|
|
# move from PhantomJS, which is deprecated, to to Selenium headless.)
|
|
unittest.main(warnings='ignore')
|