From 94c43e094cc16741495196ee05b97a6cbec6654d Mon Sep 17 00:00:00 2001 From: Richard Wall Date: Mon, 14 Sep 2015 14:33:53 +0100 Subject: [PATCH 1/8] Sketch out the new documentation --- docs/gettinginvolved/acceptance-testing.rst | 36 +++++++++++++++++++++ docs/gettinginvolved/example-acceptance.yml | 11 +++++++ 2 files changed, 47 insertions(+) diff --git a/docs/gettinginvolved/acceptance-testing.rst b/docs/gettinginvolved/acceptance-testing.rst index 0c2f6d8190..2414efeba2 100644 --- a/docs/gettinginvolved/acceptance-testing.rst +++ b/docs/gettinginvolved/acceptance-testing.rst @@ -162,6 +162,42 @@ Rackspace can use these dataset backends: admin/run-acceptance-tests --distribution centos-7 --provider rackspace --config-file config.yml +.. _acceptance-testing-openstack-config: + +OpenStack +~~~~~~~~~ + +To run the acceptance tests on Rackspace, you need: + +- An OpenStack account and the associated password or API key. +- The URL of the OpenStack keystone service. +- an ssh-key registered with the OpenStack account. + +To use the OpenStack provider, the configuration file should include an item like: + +.. code-block:: yaml + + openstack: + auth_plugin: "`password` or `apikey`" + auth_url: "https://192.168.1.101:5000/v2.0/tokens/" + region: + username: + password: + keyname: + +.. note:: If you use ``auth_plugin: apikey`` you will need to include a ``key: `` entry instead of a password. + +You will need a ssh agent running with access to the corresponding private key. + +Openstack can use these dataset backends: + + * :ref:`OpenStack`. + * :ref:`ZFS`. + * :ref:`Loopback`. + +.. prompt:: bash $ + + admin/run-acceptance-tests --distribution centos-7 --provider openstack --config-file config.yml .. _acceptance-testing-aws-config: diff --git a/docs/gettinginvolved/example-acceptance.yml b/docs/gettinginvolved/example-acceptance.yml index dc8a3f335a..68993d8445 100644 --- a/docs/gettinginvolved/example-acceptance.yml +++ b/docs/gettinginvolved/example-acceptance.yml @@ -14,6 +14,17 @@ rackspace: key: "33333333333333333333333333333333" keyname: "your.rackspace.ssh.key.identifier" +openstack: + # See ex_force_auth_version in https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html + auth_plugin: "`password` or `apikey`" + # See ex_force_auth_url in https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html + auth_url: "https://192.168.1.101:5000/v2.0/tokens/" + username: "your.openstack.username" + password: "33333333333333333333333333333333" + # OR key: " if using `auth_plugin: apikey`" + region: "RegionOne" + keyname: "your.ssh.key.identifier" + managed: addresses: - "192.0.2.15" From 38190210734b479c31e9efbd23923ef08546c103 Mon Sep 17 00:00:00 2001 From: Richard Wall Date: Mon, 14 Sep 2015 14:36:25 +0100 Subject: [PATCH 2/8] Release notes --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 3d0a93a5dd..592a672abd 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ Flocker 1.4.0.dev1 (2015-09-08) =============================== - Improved the error handling when Cinder volumes enter an unexpected state. (FLOC-2970) +- ``run-acceptance-tests`` now supports all OpenStack variants, not just RackSpace. (FLOC-3017) Flocker 1.3.0 (2015-09-04) ===================================== From 7d2e44ed726ce059b777a25b664b2394ff348052 Mon Sep 17 00:00:00 2001 From: Richard Wall Date: Mon, 14 Sep 2015 15:15:07 +0100 Subject: [PATCH 3/8] This may be enough for an OpenStack provider. --- admin/acceptance.py | 28 +++++ docs/gettinginvolved/acceptance-testing.rst | 19 ++-- docs/gettinginvolved/example-acceptance.yml | 5 + flocker/provision/__init__.py | 2 + flocker/provision/_openstack.py | 117 ++++++++++++++++++++ 5 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 flocker/provision/_openstack.py diff --git a/admin/acceptance.py b/admin/acceptance.py index a9cff1dc9d..a0a2e98201 100644 --- a/admin/acceptance.py +++ b/admin/acceptance.py @@ -791,6 +791,34 @@ def _runner_RACKSPACE(self, package_source, dataset_backend, package_source, dataset_backend, "rackspace", provider_config ) + def _runner_OPENSTACK(self, package_source, dataset_backend, + provider_config): + """ + :param PackageSource package_source: The source of omnibus packages. + :param DatasetBackend dataset_backend: A ``DatasetBackend`` constant. + :param provider_config: The ``openstack`` section of the acceptance + testing configuration file. The section of the configuration + file should look something like: + + openstack: + auth_plugin: + auth_url: + region: + tenant: + username: + secret: + keyname: + images: + ubuntu-14.04: + centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa + flavour: + + :see: :ref:`acceptance-testing-openstack-config` + """ + return self._libcloud_runner( + package_source, dataset_backend, "openstack", provider_config + ) + def _runner_AWS(self, package_source, dataset_backend, provider_config): """ diff --git a/docs/gettinginvolved/acceptance-testing.rst b/docs/gettinginvolved/acceptance-testing.rst index 2414efeba2..3060af1c13 100644 --- a/docs/gettinginvolved/acceptance-testing.rst +++ b/docs/gettinginvolved/acceptance-testing.rst @@ -171,21 +171,26 @@ To run the acceptance tests on Rackspace, you need: - An OpenStack account and the associated password or API key. - The URL of the OpenStack keystone service. -- an ssh-key registered with the OpenStack account. +- An ssh-key registered with the OpenStack account. +- The OpenStack image_id for supported Flocker node operating systems. +- The OpenStack flavour (size) to use for acceptance test nodes. To use the OpenStack provider, the configuration file should include an item like: .. code-block:: yaml openstack: - auth_plugin: "`password` or `apikey`" - auth_url: "https://192.168.1.101:5000/v2.0/tokens/" - region: + auth_plugin: + auth_url: + region: + tenant: username: - password: + secret: keyname: - -.. note:: If you use ``auth_plugin: apikey`` you will need to include a ``key: `` entry instead of a password. + images: + ubuntu-14.04: + centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa + flavour: You will need a ssh agent running with access to the corresponding private key. diff --git a/docs/gettinginvolved/example-acceptance.yml b/docs/gettinginvolved/example-acceptance.yml index 68993d8445..8d545e4e0d 100644 --- a/docs/gettinginvolved/example-acceptance.yml +++ b/docs/gettinginvolved/example-acceptance.yml @@ -23,7 +23,12 @@ openstack: password: "33333333333333333333333333333333" # OR key: " if using `auth_plugin: apikey`" region: "RegionOne" + tenant: keyname: "your.ssh.key.identifier" + images: + ubuntu-14.04: + centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa + flavour: managed: addresses: diff --git a/flocker/provision/__init__.py b/flocker/provision/__init__.py index 13452f693b..3f4b1a5d97 100644 --- a/flocker/provision/__init__.py +++ b/flocker/provision/__init__.py @@ -7,11 +7,13 @@ from ._common import PackageSource, Variants from ._install import provision, configure_cluster from ._rackspace import rackspace_provisioner +from ._openstack import openstack_provisioner from ._aws import aws_provisioner from ._ca import Certificates CLOUD_PROVIDERS = { 'rackspace': rackspace_provisioner, + 'openstack': openstack_provisioner, 'aws': aws_provisioner, } diff --git a/flocker/provision/_openstack.py b/flocker/provision/_openstack.py new file mode 100644 index 0000000000..721e5a62b3 --- /dev/null +++ b/flocker/provision/_openstack.py @@ -0,0 +1,117 @@ +# Copyright ClusterHQ Inc. See LICENSE file for details. + +""" +OpenStack provisioner. +""" + +from ._libcloud import LibcloudProvisioner +from ._install import ( + provision, + task_open_control_firewall, +) +from ._ssh import run_remotely + +from ._effect import sequence + + +def get_default_username(distribution): + """ + Return the username available by default on a system. + + :param str distribution: Name of the operating system distribution + :return str: The username made available by Rackspace for this + distribution. + """ + return 'root' + + +def provision_openstack(node, package_source, distribution, variants): + """ + Provision flocker on this node. + + :param LibcloudNode node: Node to provision. + :param PackageSource package_source: See func:`task_install_flocker` + :param bytes distribution: See func:`task_install_flocker` + :param set variants: The set of variant configurations to use when + provisioning + """ + commands = [] + commands.append(run_remotely( + username=get_default_username(distribution), + address=node.address, + commands=sequence([ + provision( + package_source=package_source, + distribution=node.distribution, + variants=variants, + ), + # https://clusterhq.atlassian.net/browse/FLOC-1550 + # This should be part of ._install.configure_cluster + task_open_control_firewall(node.distribution), + ]), + )) + + return sequence(commands) + + +IMAGE_NAMES = { + 'centos-7': u'CentOS 7 (PVHVM)', + 'ubuntu-14.04': u'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)', + 'ubuntu-15.04': u'Ubuntu 15.04 (Vivid Vervet) (PVHVM)', +} + + +def openstack_provisioner(auth_url, auth_plugin, username, secret, region, + keyname, images, flavour, tenant): + """ + Create a LibCloudProvisioner for provisioning nodes on openstack. + + :param bytes auth_url: The keystone URL. + :param bytes auth_plugin: The OpenStack authentication mechanism. One of + password or apikey. + :param bytes username: The user to connect to with. + :param bytes secret: The password or API key associated with the user. + :param bytes region: The region in which to launch the instance. + :param bytes keyname: The name of an existing ssh public key configured in + openstack. The provision step assumes the corresponding private key is + available from an agent. + :param dict images: A mapping of supported operating systems to a + corresponding OpenStack image name or image ID. + :param bytes flavour: A flavour name or flavour ID available in the target + OpenStack installation. + :param bytes tenant: The name of an OpenStack tenant or project. + """ + # Import these here, so that this can be imported without + # installng libcloud. + from libcloud.compute.providers import get_driver, Provider + + # LibCloud chooses OpenStack auth plugins using a weird naming scheme. + # See https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html#connecting-to-the-openstack-installation # noqa + auth_versions = { + "apikey": "2.0_apikey", + "password": "2.0_password", + } + # See https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html # noqa + driver = get_driver(Provider.OPENSTACK)( + key=username, + secret=secret, + region=region, + ex_force_auth_url=auth_url, + ex_force_auth_version=auth_versions[auth_plugin], + ex_force_service_region=region, + ex_tenant_name=tenant, + ) + + provisioner = LibcloudProvisioner( + driver=driver, + keyname=keyname, + image_names=images, + create_node_arguments=lambda **kwargs: { + "ex_config_drive": "true", + }, + provision=provision_openstack, + default_size=flavour, + get_default_user=get_default_username, + ) + + return provisioner From 1827ccd23667f948a06b70ee921560c7749f0069 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 14 Sep 2015 16:52:22 +0000 Subject: [PATCH 4/8] Wait for private IP rather than public IPs on devstack --- flocker/provision/_libcloud.py | 14 ++++++++++---- flocker/provision/_openstack.py | 19 +++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/flocker/provision/_libcloud.py b/flocker/provision/_libcloud.py index 1b4e2efbb9..5c1c071ba6 100644 --- a/flocker/provision/_libcloud.py +++ b/flocker/provision/_libcloud.py @@ -12,16 +12,16 @@ from flocker.provision._ssh import run_remotely, run_from_args -def get_size(driver, size_id): +def get_size(driver, size_name_or_id): """ Return a ``NodeSize`` corresponding to a given id. :param driver: The libcloud driver to query for sizes. """ try: - return [s for s in driver.list_sizes() if s.id == size_id][0] + return [s for s in driver.list_sizes() if size_name_or_id in (s.id, s.name)][0] except IndexError: - raise ValueError("Unknown size.", size_id) + raise ValueError("Unknown size.", size_name_or_id) def get_image(driver, image_name): @@ -224,7 +224,13 @@ def create_node(self, name, distribution, ) node, addresses = self._driver.wait_until_running( - [node], wait_period=15)[0] + [node], + wait_period=15, + # XXX: devstack guests don't have public_ips by default. + # Need to figure out how to handle this generally. Look at + # self.use_private_addresses + ssh_interface="private_ips", + )[0] public_address = addresses[0] diff --git a/flocker/provision/_openstack.py b/flocker/provision/_openstack.py index 721e5a62b3..076c4c4dd1 100644 --- a/flocker/provision/_openstack.py +++ b/flocker/provision/_openstack.py @@ -13,16 +13,22 @@ from ._effect import sequence +# XXX: Copied from _aws. Needs refactoring +_usernames = { + 'centos-7': 'centos', + 'ubuntu-14.04': 'ubuntu', + 'ubuntu-15.04': 'ubuntu', +} + def get_default_username(distribution): """ Return the username available by default on a system. :param str distribution: Name of the operating system distribution - :return str: The username made available by Rackspace for this - distribution. + :return str: The username made available by AWS for this distribution. """ - return 'root' + return _usernames[distribution] def provision_openstack(node, package_source, distribution, variants): @@ -54,13 +60,6 @@ def provision_openstack(node, package_source, distribution, variants): return sequence(commands) -IMAGE_NAMES = { - 'centos-7': u'CentOS 7 (PVHVM)', - 'ubuntu-14.04': u'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)', - 'ubuntu-15.04': u'Ubuntu 15.04 (Vivid Vervet) (PVHVM)', -} - - def openstack_provisioner(auth_url, auth_plugin, username, secret, region, keyname, images, flavour, tenant): """ From 3be9e8a5fc22ec9115a479c556bcb4fdafc7a460 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 15 Sep 2015 08:50:00 +0000 Subject: [PATCH 5/8] Allow root access (copied from _aws.py) --- flocker/provision/_openstack.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/flocker/provision/_openstack.py b/flocker/provision/_openstack.py index 076c4c4dd1..120e27081b 100644 --- a/flocker/provision/_openstack.py +++ b/flocker/provision/_openstack.py @@ -4,9 +4,15 @@ OpenStack provisioner. """ +from time import time + +from effect.retry import retry +from effect import Effect, Constant + from ._libcloud import LibcloudProvisioner from ._install import ( provision, + task_install_ssh_key, task_open_control_firewall, ) from ._ssh import run_remotely @@ -41,7 +47,34 @@ def provision_openstack(node, package_source, distribution, variants): :param set variants: The set of variant configurations to use when provisioning """ + username = get_default_username(distribution) commands = [] + # cloud-init may not have allowed sudo without tty yet, so try SSH key + # installation for a few more seconds: + start = [] + + # XXX Copied from _aws.py -- refactor. + def for_ten_seconds(*args, **kwargs): + if not start: + start.append(time()) + return Effect(Constant((time() - start[0]) < 30)) + + commands.append(run_remotely( + username=username, + address=node.address, + commands=retry(task_install_ssh_key(), for_ten_seconds), + )) + + commands.append(run_remotely( + username='root', + address=node.address, + commands=provision( + package_source=package_source, + distribution=node.distribution, + variants=variants, + ), + )) + commands.append(run_remotely( username=get_default_username(distribution), address=node.address, From 926606ca81b4ec6223e3fb3d6eaa4d0a3b241cc4 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 15 Sep 2015 09:02:20 +0000 Subject: [PATCH 6/8] Run the remaining steps as root --- flocker/provision/_openstack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flocker/provision/_openstack.py b/flocker/provision/_openstack.py index 120e27081b..8636eff6f7 100644 --- a/flocker/provision/_openstack.py +++ b/flocker/provision/_openstack.py @@ -76,7 +76,7 @@ def for_ten_seconds(*args, **kwargs): )) commands.append(run_remotely( - username=get_default_username(distribution), + username='root', address=node.address, commands=sequence([ provision( From f886cd3477c75584cf4403a22b863ba57a737e36 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 15 Sep 2015 14:21:43 +0000 Subject: [PATCH 7/8] Tidy up the docs and config examples --- admin/acceptance.py | 4 ++-- docs/gettinginvolved/acceptance-testing.rst | 10 +++++----- docs/gettinginvolved/example-acceptance.yml | 21 +++++++++------------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/admin/acceptance.py b/admin/acceptance.py index a0a2e98201..ab4afed38f 100644 --- a/admin/acceptance.py +++ b/admin/acceptance.py @@ -810,8 +810,8 @@ def _runner_OPENSTACK(self, package_source, dataset_backend, keyname: images: ubuntu-14.04: - centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa - flavour: + centos-7: # noqa + flavour: :see: :ref:`acceptance-testing-openstack-config` """ diff --git a/docs/gettinginvolved/acceptance-testing.rst b/docs/gettinginvolved/acceptance-testing.rst index 3060af1c13..03364f8d1d 100644 --- a/docs/gettinginvolved/acceptance-testing.rst +++ b/docs/gettinginvolved/acceptance-testing.rst @@ -167,13 +167,13 @@ Rackspace can use these dataset backends: OpenStack ~~~~~~~~~ -To run the acceptance tests on Rackspace, you need: +To run the acceptance tests on OpenStack, you need: - An OpenStack account and the associated password or API key. - The URL of the OpenStack keystone service. - An ssh-key registered with the OpenStack account. -- The OpenStack image_id for supported Flocker node operating systems. -- The OpenStack flavour (size) to use for acceptance test nodes. +- The OpenStack image name or image ID for images containing supported Flocker node operating systems. +- The OpenStack flavor to use for acceptance test nodes. To use the OpenStack provider, the configuration file should include an item like: @@ -189,8 +189,8 @@ To use the OpenStack provider, the configuration file should include an item lik keyname: images: ubuntu-14.04: - centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa - flavour: + centos-7: # noqa + flavor: You will need a ssh agent running with access to the corresponding private key. diff --git a/docs/gettinginvolved/example-acceptance.yml b/docs/gettinginvolved/example-acceptance.yml index 8d545e4e0d..41de236a3a 100644 --- a/docs/gettinginvolved/example-acceptance.yml +++ b/docs/gettinginvolved/example-acceptance.yml @@ -15,20 +15,17 @@ rackspace: keyname: "your.rackspace.ssh.key.identifier" openstack: - # See ex_force_auth_version in https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html - auth_plugin: "`password` or `apikey`" - # See ex_force_auth_url in https://libcloud.readthedocs.org/en/latest/compute/drivers/openstack.html - auth_url: "https://192.168.1.101:5000/v2.0/tokens/" - username: "your.openstack.username" - password: "33333333333333333333333333333333" - # OR key: " if using `auth_plugin: apikey`" - region: "RegionOne" - tenant: - keyname: "your.ssh.key.identifier" + auth_plugin: + auth_url: + region: + tenant: + username: + secret: + keyname: images: ubuntu-14.04: - centos-7: image_id, e.g. "327637d0-b744-4567-837e-100f01017d56"> # noqa - flavour: + centos-7: # noqa + flavor: managed: addresses: From 2d89041627b98f33a628d5310f504003ae5ee667 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 15 Sep 2015 17:40:21 +0000 Subject: [PATCH 8/8] Use flavor, fix root tty and disable firewall configuration since firewall-cmd isn't available here. --- admin/acceptance.py | 2 +- flocker/provision/_openstack.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/admin/acceptance.py b/admin/acceptance.py index ab4afed38f..1e330ebeb4 100644 --- a/admin/acceptance.py +++ b/admin/acceptance.py @@ -811,7 +811,7 @@ def _runner_OPENSTACK(self, package_source, dataset_backend, images: ubuntu-14.04: centos-7: # noqa - flavour: + flavor: :see: :ref:`acceptance-testing-openstack-config` """ diff --git a/flocker/provision/_openstack.py b/flocker/provision/_openstack.py index 8636eff6f7..bc95d7d8a8 100644 --- a/flocker/provision/_openstack.py +++ b/flocker/provision/_openstack.py @@ -4,6 +4,7 @@ OpenStack provisioner. """ +from textwrap import dedent from time import time from effect.retry import retry @@ -86,7 +87,7 @@ def for_ten_seconds(*args, **kwargs): ), # https://clusterhq.atlassian.net/browse/FLOC-1550 # This should be part of ._install.configure_cluster - task_open_control_firewall(node.distribution), + # task_open_control_firewall(node.distribution), ]), )) @@ -94,7 +95,7 @@ def for_ten_seconds(*args, **kwargs): def openstack_provisioner(auth_url, auth_plugin, username, secret, region, - keyname, images, flavour, tenant): + keyname, images, flavor, tenant): """ Create a LibCloudProvisioner for provisioning nodes on openstack. @@ -109,7 +110,7 @@ def openstack_provisioner(auth_url, auth_plugin, username, secret, region, available from an agent. :param dict images: A mapping of supported operating systems to a corresponding OpenStack image name or image ID. - :param bytes flavour: A flavour name or flavour ID available in the target + :param bytes flavor: A flavor name or flavor ID available in the target OpenStack installation. :param bytes tenant: The name of an OpenStack tenant or project. """ @@ -140,9 +141,13 @@ def openstack_provisioner(auth_url, auth_plugin, username, secret, region, image_names=images, create_node_arguments=lambda **kwargs: { "ex_config_drive": "true", + "ex_userdata": dedent("""\ + #!/bin/sh + sed -i '/Defaults *requiretty/d' /etc/sudoers + """), }, provision=provision_openstack, - default_size=flavour, + default_size=flavor, get_default_user=get_default_username, )