diff --git a/zaza/charm_lifecycle/__init__.py b/zaza/charm_lifecycle/__init__.py index e69de29..cdec784 100644 --- a/zaza/charm_lifecycle/__init__.py +++ b/zaza/charm_lifecycle/__init__.py @@ -0,0 +1 @@ +"""Modules for running lifecycle phases.""" diff --git a/zaza/charm_lifecycle/collect.py b/zaza/charm_lifecycle/collect.py index e69de29..be1bd36 100644 --- a/zaza/charm_lifecycle/collect.py +++ b/zaza/charm_lifecycle/collect.py @@ -0,0 +1 @@ +"""Run collect phase.""" diff --git a/zaza/charm_lifecycle/configure.py b/zaza/charm_lifecycle/configure.py index 7ff17fa..50000c0 100644 --- a/zaza/charm_lifecycle/configure.py +++ b/zaza/charm_lifecycle/configure.py @@ -1,3 +1,4 @@ +"""Run configuration phase.""" import asyncio import argparse import logging @@ -7,8 +8,10 @@ import zaza.charm_lifecycle.utils as utils def run_configure_list(functions): - """Run the configure scripts as defined in the list of test classes in - series. + """Run the configure scripts. + + Run the configure scripts as defined in the list of test classes in + series. :param functions: List of configure functions functions :type tests: ['zaza.charms_tests.svc.setup', ...] @@ -18,16 +21,17 @@ def run_configure_list(functions): def configure(model_name, functions): - """Run all post-deployment configuration steps + """Run all post-deployment configuration steps. :param functions: List of configure functions functions - :type tests: ['zaza.charms_tests.svc.setup', ...]""" + :type tests: ['zaza.charms_tests.svc.setup', ...] + """ utils.set_juju_model(model_name) run_configure_list(functions) def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -44,9 +48,12 @@ def parse_args(args): def main(): - """Run the configuration defined by the command line args or if none were - provided read the configuration functions from the charms tests.yaml - config file""" + """Run the configuration defined by the command line args. + + Run the configuration defined by the command line args or if none were + provided read the configuration functions from the charms tests.yaml + config file + """ logging.basicConfig(level=logging.INFO) args = parse_args(sys.argv[1:]) funcs = args.configfuncs or utils.get_charm_config()['configure'] diff --git a/zaza/charm_lifecycle/deploy.py b/zaza/charm_lifecycle/deploy.py index d12d2c1..8ba3c61 100755 --- a/zaza/charm_lifecycle/deploy.py +++ b/zaza/charm_lifecycle/deploy.py @@ -1,3 +1,4 @@ +"""Run deploy phase.""" import argparse import jinja2 import logging @@ -26,7 +27,7 @@ applications: def is_valid_env_key(key): - """Check if key is a valid environment variable name for use with template + """Check if key is a valid environment variable name for use with template. :param key: List of configure functions functions :type key: str @@ -42,8 +43,7 @@ def is_valid_env_key(key): def get_template_context_from_env(): - """Return environment variables from the current environment that can be - used for template rendering. + """Return environment vars from the current env for template rendering. :returns: Environment variable key values for use with template rendering :rtype: dict @@ -52,7 +52,7 @@ def get_template_context_from_env(): def get_charm_config_context(): - """Return settings from charm config file + """Return settings from charm config file. :returns: Context for template rendering :rtype: dict @@ -65,7 +65,7 @@ def get_charm_config_context(): def get_template_overlay_context(): - """Combine contexts which can be used for overlay template rendering + """Combine contexts which can be used for overlay template rendering. :returns: Context for template rendering :rtype: dict @@ -101,8 +101,10 @@ def get_jinja2_env(): def get_template_name(target_file): - """Return the expected name of the template used to generate the - target_file + """Return the template name for target_file. + + Return the expected name of the template used to generate the + target_file :param target_file: File to be rendered :type target_file: str @@ -113,7 +115,7 @@ def get_template_name(target_file): def get_template(target_file): - """Return the jinja2 template for the given file + """Return the jinja2 template for the given file. :returns: Template object used to generate target_file :rtype: jinja2.Template @@ -127,7 +129,7 @@ def get_template(target_file): def render_template(template, target_file): - """Render the template to the file supplied + """Render the template to the file supplied. :param template: Template to be rendered :type template: jinja2.Template @@ -140,7 +142,7 @@ def render_template(template, target_file): def render_overlay(overlay_name, target_dir): - """Render the overlay template in the directory supplied + """Render the overlay template in the directory supplied. :param overlay_name: Name of overlay to be rendered :type overlay_name: str @@ -160,7 +162,7 @@ def render_overlay(overlay_name, target_dir): def render_local_overlay(target_dir): - """Render the local overlay template in the directory supplied + """Render the local overlay template in the directory supplied. :param target_dir: Directory to render overlay in :type overlay_name: str @@ -177,7 +179,7 @@ def render_local_overlay(target_dir): def render_overlays(bundle, target_dir): - """Render the overlays for the given bundle in the directory provided + """Render the overlays for the given bundle in the directory provided. :param bundle: Name of bundle being deployed :type bundle: str @@ -194,7 +196,7 @@ def render_overlays(bundle, target_dir): def deploy_bundle(bundle, model): - """Deploy the given bundle file in the specified model + """Deploy the given bundle file in the specified model. :param bundle: Path to bundle file :type bundle: str @@ -210,7 +212,7 @@ def deploy_bundle(bundle, model): def deploy(bundle, model, wait=True): - """Run all steps to complete deployment + """Run all steps to complete deployment. :param bundle: Path to bundle file :type bundle: str @@ -230,7 +232,7 @@ def deploy(bundle, model, wait=True): def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -252,7 +254,7 @@ def parse_args(args): def main(): - """Deploy bundle""" + """Deploy bundle.""" logging.basicConfig(level=logging.INFO) args = parse_args(sys.argv[1:]) deploy(args.bundle, args.model, wait=args.wait) diff --git a/zaza/charm_lifecycle/destroy.py b/zaza/charm_lifecycle/destroy.py index d7425c6..290d1c4 100644 --- a/zaza/charm_lifecycle/destroy.py +++ b/zaza/charm_lifecycle/destroy.py @@ -1,3 +1,4 @@ +"""Run destroy phase.""" import argparse import logging import sys @@ -6,7 +7,7 @@ import zaza.controller def destroy(model_name): - """Run all steps to cleaup after a test run + """Run all steps to cleaup after a test run. :param model: Name of model to remove :type bundle: str @@ -15,7 +16,7 @@ def destroy(model_name): def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -29,7 +30,7 @@ def parse_args(args): def main(): - """Cleanup after test run""" + """Cleanup after test run.""" logging.basicConfig(level=logging.INFO) args = parse_args(sys.argv[1:]) destroy(args.model_name) diff --git a/zaza/charm_lifecycle/func_test_runner.py b/zaza/charm_lifecycle/func_test_runner.py index 3969d0d..80abd82 100644 --- a/zaza/charm_lifecycle/func_test_runner.py +++ b/zaza/charm_lifecycle/func_test_runner.py @@ -1,3 +1,4 @@ +"""Run full test lifecycle.""" import argparse import asyncio import logging @@ -14,11 +15,16 @@ import zaza.charm_lifecycle.test as test def generate_model_name(): + """Generate a unique model name. + + :returns: Model name + :rtype: str + """ return 'zaza-{}'.format(str(uuid.uuid4())[-12:]) def func_test_runner(keep_model=False, smoke=False, bundle=None): - """Deploy the bundles and run the tests as defined by the charms tests.yaml + """Deploy the bundles and run the tests as defined by the charms tests.yaml. :param keep_model: Whether to destroy model at end of run :type keep_model: boolean @@ -58,7 +64,7 @@ def func_test_runner(keep_model=False, smoke=False, bundle=None): def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -82,6 +88,7 @@ def parse_args(args): def main(): + """Execute full test run.""" args = parse_args(sys.argv[1:]) level = getattr(logging, args.loglevel.upper(), None) diff --git a/zaza/charm_lifecycle/prepare.py b/zaza/charm_lifecycle/prepare.py index c3fd1a0..900b4ef 100644 --- a/zaza/charm_lifecycle/prepare.py +++ b/zaza/charm_lifecycle/prepare.py @@ -1,3 +1,4 @@ +"""Run prepare phase.""" import argparse import logging import sys @@ -21,7 +22,7 @@ MODEL_DEFAULTS = { def prepare(model_name): - """Run all steps to prepare the environment before a functional test run + """Run all steps to prepare the environment before a functional test run. :param model: Name of model to add :type bundle: str @@ -30,7 +31,7 @@ def prepare(model_name): def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -44,7 +45,7 @@ def parse_args(args): def main(): - """Add a new model""" + """Add a new model.""" logging.basicConfig(level=logging.INFO) args = parse_args(sys.argv[1:]) prepare(args.model_name) diff --git a/zaza/charm_lifecycle/test.py b/zaza/charm_lifecycle/test.py index 65c8f6b..ab11c77 100644 --- a/zaza/charm_lifecycle/test.py +++ b/zaza/charm_lifecycle/test.py @@ -1,3 +1,4 @@ +"""Run test phase.""" import asyncio import argparse import logging @@ -22,13 +23,13 @@ def run_test_list(tests): def test(model_name, tests): - """Run all steps to execute tests against the model""" + """Run all steps to execute tests against the model.""" utils.set_juju_model(model_name) run_test_list(tests) def parse_args(args): - """Parse command line arguments + """Parse command line arguments. :param args: List of configure functions functions :type list: [str1, str2,...] List of command line arguments @@ -45,8 +46,11 @@ def parse_args(args): def main(): - """Run the tests defined by the command line args or if none were provided - read the tests from the charms tests.yaml config file""" + """Run the tests defined by the command line args. + + Run the tests defined by the command line args or if none were provided + read the tests from the charms tests.yaml config file + """ logging.basicConfig(level=logging.INFO) args = parse_args(sys.argv[1:]) tests = args.tests or utils.get_charm_config()['tests'] diff --git a/zaza/charm_lifecycle/utils.py b/zaza/charm_lifecycle/utils.py index 571053f..8f4c92b 100644 --- a/zaza/charm_lifecycle/utils.py +++ b/zaza/charm_lifecycle/utils.py @@ -1,3 +1,4 @@ +"""Utilities to support running lifecycle phases.""" import importlib import os import yaml @@ -9,7 +10,7 @@ DEFAULT_TEST_CONFIG = "./tests/tests.yaml" def get_charm_config(yaml_file=None): - """Read the yaml test config file and return the resulting config + """Read the yaml test config file and return the resulting config. :param yaml_file: File to be read :type yaml_file: str @@ -23,10 +24,10 @@ def get_charm_config(yaml_file=None): def get_class(class_str): - """Get the class represented by the given string + """Get the class represented by the given string. - For example, get_class('zaza.charms_tests.svc.TestSVCClass1') - returns zaza.charms_tests.svc.TestSVCClass1 + For example, get_class('zaza.charms_tests.svc.TestSVCClass1') + returns zaza.charms_tests.svc.TestSVCClass1 :param class_str: Class to be returned :type class_str: str @@ -40,7 +41,7 @@ def get_class(class_str): def set_juju_model(model_name): - """Point environment at the given model + """Point environment at the given model. :param model_name: Model to point environment at :type model_name: str @@ -49,7 +50,7 @@ def set_juju_model(model_name): def get_juju_model(): - """Retrieve current model + """Retrieve current model. First check the environment for JUJU_MODEL. If this is not set, get the current active model. @@ -57,7 +58,6 @@ def get_juju_model(): :returns: In focus model name :rtype: str """ - try: # Check the environment return os.environ["JUJU_MODEL"] diff --git a/zaza/charm_tests/vault/setup.py b/zaza/charm_tests/vault/setup.py index f5437d7..9db4de0 100644 --- a/zaza/charm_tests/vault/setup.py +++ b/zaza/charm_tests/vault/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +"""Run configuration phase.""" import zaza.charm_tests.vault.utils as vault_utils diff --git a/zaza/utilities/__init__.py b/zaza/utilities/__init__.py index e69de29..56ea2bc 100644 --- a/zaza/utilities/__init__.py +++ b/zaza/utilities/__init__.py @@ -0,0 +1 @@ +"""Collection of utilities to support zaza tests etc.""" diff --git a/zaza/utilities/cert.py b/zaza/utilities/cert.py index 16fc406..f721c9e 100644 --- a/zaza/utilities/cert.py +++ b/zaza/utilities/cert.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # # Copyright 2018 Canonical Ltd. # @@ -13,6 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Module for working with x.509 certificates.""" import cryptography from cryptography.hazmat.primitives.asymmetric import rsa @@ -29,7 +29,7 @@ def generate_cert(common_name, signing_key=None, signing_key_password=None, generate_ca=False): - """Generate x.509 certificate + """Generate x.509 certificate. Example of how to create a certificate chain:: diff --git a/zaza/utilities/cli.py b/zaza/utilities/cli.py index bace523..1dbf7a7 100644 --- a/zaza/utilities/cli.py +++ b/zaza/utilities/cli.py @@ -1,11 +1,11 @@ -#!/usr/bin/env python3 +"""Module containing utilities for working with commandline tools.""" import logging import os def parse_arg(options, arg, multiargs=False): - """Parse argparse argments + """Parse argparse argments. :param options: Argparse options :type options: argparse object @@ -16,7 +16,6 @@ def parse_arg(options, arg, multiargs=False): :returns: Argparse atrribute value :rtype: string """ - if arg.upper() in os.environ: if multiargs: return os.environ[arg.upper()].split() @@ -27,12 +26,11 @@ def parse_arg(options, arg, multiargs=False): def setup_logging(): - """Setup zaza logging + """Do setup for logging. :returns: Nothing: This fucntion is executed for its sideffect :rtype: None """ - logFormatter = logging.Formatter( fmt="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") diff --git a/zaza/utilities/exceptions.py b/zaza/utilities/exceptions.py index ee5fbb0..ef48130 100644 --- a/zaza/utilities/exceptions.py +++ b/zaza/utilities/exceptions.py @@ -1,3 +1,7 @@ +"""Module of exceptions that zaza may raise.""" + class MissingOSAthenticationException(Exception): + """Exception when some data needed to authenticate is missing.""" + pass diff --git a/zaza/utilities/generic.py b/zaza/utilities/generic.py index 62b5fc5..24167c5 100644 --- a/zaza/utilities/generic.py +++ b/zaza/utilities/generic.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +"""Collection of functions that did not fit anywhere else.""" import logging import os @@ -10,20 +10,21 @@ from zaza.utilities import juju as juju_utils def dict_to_yaml(dict_data): - """Return YAML from dictionary + """Return YAML from dictionary. :param dict_data: Dictionary data :type dict_data: dict :returns: YAML dump :rtype: string """ - return yaml.dump(dict_data, default_flow_style=False) def get_network_config(net_topology, ignore_env_vars=False, net_topology_file="network.yaml"): - """Get network info from network.yaml, override the values if specific + """Get network info from environment. + + Get network info from network.yaml, override the values if specific environment variables are set for the undercloud. This function may be used when running network configuration from CLI to @@ -36,7 +37,6 @@ def get_network_config(net_topology, ignore_env_vars=False, :returns: Dictionary of network configuration :rtype: dict """ - if os.path.exists(net_topology_file): net_info = get_yaml_config(net_topology_file)[net_topology] else: @@ -53,7 +53,7 @@ def get_network_config(net_topology, ignore_env_vars=False, def get_pkg_version(application, pkg): - """Return package version + """Return package version. :param application: Application name :type application: string @@ -62,7 +62,6 @@ def get_pkg_version(application, pkg): :returns: List of package version :rtype: list """ - versions = [] units = model.get_units( lifecycle_utils.get_juju_model(), application) @@ -76,7 +75,9 @@ def get_pkg_version(application, pkg): def get_undercloud_env_vars(): - """ Get environment specific undercloud network configuration settings from + """Get environment specific undercloud network configuration settings. + + Get environment specific undercloud network configuration settings from environment variables. For each testing substrate, specific undercloud network configuration @@ -106,7 +107,6 @@ def get_undercloud_env_vars(): :returns: Network environment variables :rtype: dict """ - # Handle backward compatibile OSCI enviornment variables _vars = {} _vars['net_id'] = os.environ.get('NET_ID') @@ -141,14 +141,13 @@ def get_undercloud_env_vars(): def get_yaml_config(config_file): - """Return configuration from YAML file + """Return configuration from YAML file. :param config_file: Configuration file name :type config_file: string :returns: Dictionary of configuration :rtype: dict """ - # Note in its original form get_mojo_config it would do a search pattern # through mojo stage directories. This version assumes the yaml file is in # the pwd. diff --git a/zaza/utilities/juju.py b/zaza/utilities/juju.py index 66f5521..31d2ab6 100644 --- a/zaza/utilities/juju.py +++ b/zaza/utilities/juju.py @@ -13,6 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Module for interacting with juju.""" import os from pathlib import Path import yaml @@ -26,7 +27,7 @@ from zaza.utilities import generic as generic_utils def get_application_status(application=None, unit=None): - """Return the juju status for an application + """Return the juju status for an application. :param application: Application name :type application: string @@ -35,7 +36,6 @@ def get_application_status(application=None, unit=None): :returns: Juju status output for an application :rtype: dict """ - status = get_full_juju_status() if unit and not application: @@ -49,7 +49,7 @@ def get_application_status(application=None, unit=None): def get_cloud_configs(cloud=None): - """Get cloud configuration from local clouds.yaml + """Get cloud configuration from local clouds.yaml. libjuju does not yet have cloud information implemented. Use libjuju as soon as possible. @@ -59,7 +59,6 @@ def get_cloud_configs(cloud=None): :returns: Dictionary of cloud configuration :rtype: dict """ - home = str(Path.home()) cloud_config = os.path.join(home, ".local", "share", "juju", "clouds.yaml") if cloud: @@ -69,25 +68,23 @@ def get_cloud_configs(cloud=None): def get_full_juju_status(): - """Return the full juju status output + """Return the full juju status output. :returns: Full juju status output :rtype: dict """ - status = model.get_status(lifecycle_utils.get_juju_model()) return status def get_machines_for_application(application): - """Return machines for a given application + """Return machines for a given application. :param application: Application name :type application: string :returns: List of machines for an application :rtype: list """ - status = get_application_status(application) # libjuju juju status no longer has units for subordinate charms @@ -103,7 +100,7 @@ def get_machines_for_application(application): def get_machine_status(machine, key=None): - """Return the juju status for a machine + """Return the juju status for a machine. :param machine: Machine number :type machine: string @@ -112,7 +109,6 @@ def get_machine_status(machine, key=None): :returns: Juju status output for a machine :rtype: dict """ - status = get_full_juju_status() status = status.machines.get(machine) if key: @@ -121,14 +117,13 @@ def get_machine_status(machine, key=None): def get_machine_uuids_for_application(application): - """Return machine uuids for a given application + """Return machine uuids for a given application. :param application: Application name :type application: string :returns: List of machine uuuids for an application :rtype: list """ - uuids = [] for machine in get_machines_for_application(application): uuids.append(get_machine_status(machine, key="instance-id")) @@ -136,12 +131,11 @@ def get_machine_uuids_for_application(application): def get_provider_type(): - """Get the type of the undercloud + """Get the type of the undercloud. :returns: Name of the undercloud type :rtype: string """ - cloud = controller.get_cloud() if cloud: # If the controller was deployed from this system with @@ -155,7 +149,7 @@ def get_provider_type(): def remote_run(unit, remote_cmd, timeout=None, fatal=None): - """Run command on unit and return the output + """Run command on unit and return the output. NOTE: This function is pre-deprecated. As soon as libjuju unit.run is able to return output this functionality should move to model.run_on_unit. @@ -186,7 +180,8 @@ def remote_run(unit, remote_cmd, timeout=None, fatal=None): def _get_unit_names(names): - """ + """Resolve given application names to first unit name of said application. + Helper function that resolves application names to first unit name of said application. Any already resolved unit names are returned as-is. @@ -207,7 +202,8 @@ def _get_unit_names(names): def get_relation_from_unit(entity, remote_entity, remote_interface_name): - """ + """Get relation data passed between two units. + Get relation data for relation with `remote_interface_name` between `entity` and `remote_entity` from the perspective of `entity`. diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index deee473..b0ea06e 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -1,3 +1,7 @@ +"""Module for interacting with OpenStack. + +This module contains a number of functions for interacting with Openstack. +""" from .os_versions import ( OPENSTACK_CODENAMES, SWIFT_CODENAMES, @@ -82,7 +86,7 @@ UPGRADE_SERVICES = [ # Openstack Client helpers def get_ks_creds(cloud_creds, scope='PROJECT'): - """Return the credentials for authenticating against keystone + """Return the credentials for authenticating against keystone. :param cloud_creds: Openstack RC environment credentials :type cloud_creds: dict @@ -91,7 +95,6 @@ def get_ks_creds(cloud_creds, scope='PROJECT'): :returns: Credentials dictionary :rtype: dict """ - if cloud_creds.get('API_VERSION', 2) == 2: auth = { 'username': cloud_creds['OS_USERNAME'], @@ -122,7 +125,7 @@ def get_ks_creds(cloud_creds, scope='PROJECT'): def get_glance_session_client(session): - """Return glanceclient authenticated by keystone session + """Return glanceclient authenticated by keystone session. :param session: Keystone session object :type session: keystoneauth1.session.Session object @@ -133,36 +136,33 @@ def get_glance_session_client(session): def get_nova_session_client(session): - """Return novaclient authenticated by keystone session + """Return novaclient authenticated by keystone session. :param session: Keystone session object :type session: keystoneauth1.session.Session object :returns: Authenticated novaclient :rtype: novaclient.Client object """ - return novaclient_client.Client(2, session=session) def get_neutron_session_client(session): - """Return neutronclient authenticated by keystone session + """Return neutronclient authenticated by keystone session. :param session: Keystone session object :type session: keystoneauth1.session.Session object :returns: Authenticated neutronclient :rtype: neutronclient.Client object """ - return neutronclient.Client(session=session) def get_keystone_scope(): - """Return Keystone scope based on OpenStack release + """Return Keystone scope based on OpenStack release. :returns: String keystone scope :rtype: string """ - os_version = get_current_os_versions("keystone")["keystone"] # Keystone policy.json shipped the charm with liberty requires a domain # scoped token. Bug #1649106 @@ -174,7 +174,7 @@ def get_keystone_scope(): def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'): - """Return keystone session + """Return keystone session. :param openrc_creds: Openstack RC credentials :type openrc_creds: dict @@ -185,7 +185,6 @@ def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'): :returns: Keystone session object :rtype: keystoneauth1.session.Session object """ - keystone_creds = get_ks_creds(opentackrc_creds, scope=scope) if opentackrc_creds.get('API_VERSION', 2) == 2: auth = v2.Password(**keystone_creds) @@ -195,41 +194,38 @@ def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'): def get_overcloud_keystone_session(): - """Return Over cloud keystone session + """Return Over cloud keystone session. :returns keystone_session: keystoneauth1.session.Session object :rtype: keystoneauth1.session.Session """ - return get_keystone_session(get_overcloud_auth(), scope=get_keystone_scope()) def get_undercloud_keystone_session(): - """Return Under cloud keystone session + """Return Under cloud keystone session. :returns keystone_session: keystoneauth1.session.Session object :rtype: keystoneauth1.session.Session """ - return get_keystone_session(get_undercloud_auth(), scope=get_keystone_scope()) def get_keystone_session_client(session): - """Return keystoneclient authenticated by keystone session + """Return keystoneclient authenticated by keystone session. :param session: Keystone session object :type session: keystoneauth1.session.Session object :returns: Authenticated keystoneclient :rtype: keystoneclient.v3.Client object """ - return keystoneclient_v3.Client(session=session) def get_keystone_client(opentackrc_creds, insecure=True): - """Return authenticated keystoneclient and set auth_ref for service_catalog + """Return authenticated keystoneclient and set auth_ref for service_catalog. :param openrc_creds: Openstack RC credentials :type openrc_creds: dict @@ -238,7 +234,6 @@ def get_keystone_client(opentackrc_creds, insecure=True): :returns: Authenticated keystoneclient :rtype: keystoneclient.v3.Client object """ - session = get_keystone_session(opentackrc_creds, insecure) client = get_keystone_session_client(session) keystone_creds = get_ks_creds(opentackrc_creds) @@ -252,7 +247,7 @@ def get_keystone_client(opentackrc_creds, insecure=True): def get_project_id(ks_client, project_name, api_version=2, domain_name=None): - """Return project ID + """Return project ID. :param ks_client: Authenticated keystoneclient :type ks_client: keystoneclient.v3.Client object @@ -265,7 +260,6 @@ def get_project_id(ks_client, project_name, api_version=2, domain_name=None): :returns: Project ID :rtype: string or None """ - domain_id = None if domain_name: domain_id = ks_client.domains.list(name=domain_name)[0].id @@ -278,22 +272,20 @@ def get_project_id(ks_client, project_name, api_version=2, domain_name=None): # Neutron Helpers def get_gateway_uuids(): - """Return machine uuids for neutron-gateway(s) + """Return machine uuids for neutron-gateway(s). :returns: List of uuids :rtype: list """ - return juju_utils.get_machine_uuids_for_application('neutron-gateway') def get_ovs_uuids(): - """Return machine uuids for neutron-openvswitch(s) + """Return machine uuids for neutron-openvswitch(s). :returns: List of uuids :rtype: list """ - return (juju_utils .get_machine_uuids_for_application('neutron-openvswitch')) @@ -303,14 +295,13 @@ NEW_STYLE_NETWORKING = 'physnet1:br-ex' def deprecated_external_networking(dvr_mode=False): - """Determine whether deprecated external network mode is in use + """Determine whether deprecated external network mode is in use. :param dvr_mode: Using DVR mode or not :type dvr_mode: boolean :returns: True or False :rtype: boolean """ - bridge_mappings = None if dvr_mode: bridge_mappings = get_application_config_option('neutron-openvswitch', @@ -325,7 +316,7 @@ def deprecated_external_networking(dvr_mode=False): def get_net_uuid(neutron_client, net_name): - """Determine whether deprecated external network mode is in use + """Determine whether deprecated external network mode is in use. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -334,20 +325,18 @@ def get_net_uuid(neutron_client, net_name): :returns: Network ID :rtype: string """ - network = neutron_client.list_networks(name=net_name)['networks'][0] return network['id'] def get_admin_net(neutron_client): - """Return admin netowrk + """Return admin netowrk. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object :returns: Admin network object :rtype: dict """ - for net in neutron_client.list_networks()['networks']: if net['name'].endswith('_admin_net'): return net @@ -355,7 +344,7 @@ def get_admin_net(neutron_client): def configure_gateway_ext_port(novaclient, neutronclient, dvr_mode=None, net_id=None): - """Configure the neturong-gateway external port + """Configure the neturong-gateway external port. :param novaclient: Authenticated novaclient :type novaclient: novaclient.Client object @@ -366,7 +355,6 @@ def configure_gateway_ext_port(novaclient, neutronclient, :param net_id: Network ID :type net_id: string """ - if dvr_mode: uuids = get_ovs_uuids() else: @@ -432,7 +420,7 @@ def configure_gateway_ext_port(novaclient, neutronclient, def create_project_network(neutron_client, project_id, net_name='private', shared=False, network_type='gre', domain=None): - """Create the project network + """Create the project network. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -449,7 +437,6 @@ def create_project_network(neutron_client, project_id, net_name='private', :returns: Network object :rtype: dict """ - networks = neutron_client.list_networks(name=net_name) if len(networks['networks']) == 0: logging.info('Creating network: %s', @@ -473,7 +460,7 @@ def create_project_network(neutron_client, project_id, net_name='private', def create_external_network(neutron_client, project_id, dvr_mode, net_name='ext_net'): - """Create the external network + """Create the external network. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -486,7 +473,6 @@ def create_external_network(neutron_client, project_id, dvr_mode, :returns: Network object :rtype: dict """ - networks = neutron_client.list_networks(name=net_name) if len(networks['networks']) == 0: logging.info('Configuring external network') @@ -512,7 +498,7 @@ def create_external_network(neutron_client, project_id, dvr_mode, def create_project_subnet(neutron_client, project_id, network, cidr, dhcp=True, subnet_name='private_subnet', domain=None, subnetpool=None, ip_version=4, prefix_len=24): - """Create the project subnet + """Create the project subnet. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -537,7 +523,6 @@ def create_project_subnet(neutron_client, project_id, network, cidr, dhcp=True, :returns: Subnet object :rtype: dict """ - # Create subnet subnets = neutron_client.list_subnets(name=subnet_name) if len(subnets['subnets']) == 0: @@ -567,7 +552,7 @@ def create_external_subnet(neutron_client, project_id, network, default_gateway=None, cidr=None, start_floating_ip=None, end_floating_ip=None, subnet_name='ext_net_subnet'): - """Create the external subnet + """Create the external subnet. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -588,7 +573,6 @@ def create_external_subnet(neutron_client, project_id, network, :returns: Subnet object :rtype: dict """ - subnets = neutron_client.list_subnets(name=subnet_name) if len(subnets['subnets']) == 0: subnet_msg = { @@ -620,7 +604,7 @@ def create_external_subnet(neutron_client, project_id, network, def update_subnet_dns(neutron_client, subnet, dns_servers): - """Update subnet DNS servers + """Update subnet DNS servers. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -629,7 +613,6 @@ def update_subnet_dns(neutron_client, subnet, dns_servers): :param dns_servers: Comma separted list of IP addresses :type project_id: string """ - msg = { 'subnet': { 'dns_nameservers': dns_servers.split(',') @@ -641,7 +624,7 @@ def update_subnet_dns(neutron_client, subnet, dns_servers): def create_provider_router(neutron_client, project_id): - """Create the provider router + """Create the provider router. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -650,7 +633,6 @@ def create_provider_router(neutron_client, project_id): :returns: Router object :rtype: dict """ - routers = neutron_client.list_routers(name='provider-router') if len(routers['routers']) == 0: logging.info('Creating provider router for external network access') @@ -669,7 +651,7 @@ def create_provider_router(neutron_client, project_id): def plug_extnet_into_router(neutron_client, router, network): - """Add external interface to virtual router + """Add external interface to virtual router. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -678,7 +660,6 @@ def plug_extnet_into_router(neutron_client, router, network): :param network: Network object :type network: dict """ - ports = neutron_client.list_ports(device_owner='network:router_gateway', network_id=network['id']) if len(ports['ports']) == 0: @@ -692,7 +673,7 @@ def plug_extnet_into_router(neutron_client, router, network): def plug_subnet_into_router(neutron_client, router, network, subnet): - """Add subnet interface to virtual router + """Add subnet interface to virtual router. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -703,7 +684,6 @@ def plug_subnet_into_router(neutron_client, router, network, subnet): :param subnet: Subnet object :type subnet: dict """ - routers = neutron_client.list_routers(name=router) if len(routers['routers']) == 0: logging.error('Unable to locate provider router %s', router) @@ -723,7 +703,7 @@ def plug_subnet_into_router(neutron_client, router, network, subnet): def create_address_scope(neutron_client, project_id, name, ip_version=4): - """Create address scope + """Create address scope. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -736,7 +716,6 @@ def create_address_scope(neutron_client, project_id, name, ip_version=4): :returns: Address scope object :rtype: dict """ - address_scopes = neutron_client.list_address_scopes(name=name) if len(address_scopes['address_scopes']) == 0: logging.info('Creating {} address scope'.format(name)) @@ -759,7 +738,7 @@ def create_address_scope(neutron_client, project_id, name, ip_version=4): def create_subnetpool(neutron_client, project_id, name, subnetpool_prefix, address_scope, shared=True): - """Create subnet pool + """Create subnet pool. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -776,7 +755,6 @@ def create_subnetpool(neutron_client, project_id, name, subnetpool_prefix, :returns: Subnetpool object :rtype: dict """ - subnetpools = neutron_client.list_subnetpools(name=name) if len(subnetpools['subnetpools']) == 0: logging.info('Creating subnetpool: %s', @@ -800,7 +778,7 @@ def create_subnetpool(neutron_client, project_id, name, subnetpool_prefix, def create_bgp_speaker(neutron_client, local_as=12345, ip_version=4, name='bgp-speaker'): - """Create BGP speaker + """Create BGP speaker. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -813,7 +791,6 @@ def create_bgp_speaker(neutron_client, local_as=12345, ip_version=4, :returns: BGP speaker object :rtype: dict """ - bgp_speakers = neutron_client.list_bgp_speakers(name=name) if len(bgp_speakers['bgp_speakers']) == 0: logging.info('Creating BGP Speaker') @@ -833,7 +810,7 @@ def create_bgp_speaker(neutron_client, local_as=12345, ip_version=4, def add_network_to_bgp_speaker(neutron_client, bgp_speaker, network_name): - """Advertise network on BGP Speaker + """Advertise network on BGP Speaker. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -842,7 +819,6 @@ def add_network_to_bgp_speaker(neutron_client, bgp_speaker, network_name): :param network_name: Name of network to advertise :type network_name: string """ - network_id = get_net_uuid(neutron_client, network_name) # There is no direct way to determine which networks have already # been advertised. For example list_route_advertised_from_bgp_speaker shows @@ -859,7 +835,7 @@ def add_network_to_bgp_speaker(neutron_client, bgp_speaker, network_name): def create_bgp_peer(neutron_client, peer_application_name='quagga', remote_as=10000, auth_type='none'): - """Create BGP peer + """Create BGP peer. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -872,7 +848,6 @@ def create_bgp_peer(neutron_client, peer_application_name='quagga', :returns: BGP peer object :rtype: dict """ - peer_unit = model.get_units( lifecycle_utils.get_juju_model(), peer_application_name)[0] peer_ip = peer_unit.public_address @@ -895,7 +870,7 @@ def create_bgp_peer(neutron_client, peer_application_name='quagga', def add_peer_to_bgp_speaker(neutron_client, bgp_speaker, bgp_peer): - """Add BGP peer relationship to BGP speaker + """Add BGP peer relationship to BGP speaker. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -904,7 +879,6 @@ def add_peer_to_bgp_speaker(neutron_client, bgp_speaker, bgp_peer): :param bpg_peer: BGP peer object :type bgp_peer: dict """ - # Handle the expected exception if the peer is already on the # speaker try: @@ -918,14 +892,13 @@ def add_peer_to_bgp_speaker(neutron_client, bgp_speaker, bgp_peer): def add_neutron_secgroup_rules(neutron_client, project_id): - """Add neutron security group rules + """Add neutron security group rules. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object :param project_id: Project ID :type project_id: string """ - secgroup = None for group in neutron_client.list_security_groups().get('security_groups'): if (group.get('name') == 'default' and @@ -968,7 +941,7 @@ def add_neutron_secgroup_rules(neutron_client, project_id): def create_port(neutron_client, name, network_name): - """Create port on network + """Create port on network. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -979,7 +952,6 @@ def create_port(neutron_client, name, network_name): :returns: Port object :rtype: dict """ - ports = neutron_client.list_ports(name=name) if len(ports['ports']) == 0: logging.info('Creating port: {}'.format(name)) @@ -999,7 +971,7 @@ def create_port(neutron_client, name, network_name): def create_floating_ip(neutron_client, network_name, port=None): - """Create floating IP on network and optionally associate to a port + """Create floating IP on network and optionally associate to a port. :param neutron_client: Authenticated neutronclient :type neutron_client: neutronclient.Client object @@ -1010,7 +982,6 @@ def create_floating_ip(neutron_client, network_name, port=None): :returns: Floating IP object :rtype: dict """ - floatingips = neutron_client.list_floatingips() if len(floatingips['floatingips']) > 0: if port: @@ -1038,20 +1009,19 @@ def create_floating_ip(neutron_client, network_name, port=None): # Codename and package versions def get_swift_codename(version): - """Determine OpenStack codename that corresponds to swift version + """Determine OpenStack codename that corresponds to swift version. :param version: Version of Swift :type version: string :returns: Codename for swift :rtype: string """ - codenames = [k for k, v in six.iteritems(SWIFT_CODENAMES) if version in v] return codenames[0] def get_os_code_info(package, pkg_version): - """Determine OpenStack codename that corresponds to package version + """Determine OpenStack codename that corresponds to package version. :param package: Package name :type package: string @@ -1060,7 +1030,6 @@ def get_os_code_info(package, pkg_version): :returns: Codename for package :rtype: string """ - # {'code_num': entry, 'code_name': OPENSTACK_CODENAMES[entry]} # Remove epoch if it exists if ':' in pkg_version: @@ -1090,14 +1059,13 @@ def get_os_code_info(package, pkg_version): def get_current_os_versions(deployed_applications): - """Determine OpenStack codename of deployed applications + """Determine OpenStack codename of deployed applications. :param deployed_applications: List of deployed applications :type deployed_applications: list :returns: List of aplication to codenames dictionaries :rtype: list """ - versions = {} for application in UPGRADE_SERVICES: if application['name'] not in deployed_applications: @@ -1111,21 +1079,20 @@ def get_current_os_versions(deployed_applications): def get_application_config_keys(application): - """Return application configuration keys + """Return application configuration keys. :param application: Name of application :type application: string :returns: List of aplication configuration keys :rtype: list """ - application_config = model.get_application_config( lifecycle_utils.get_juju_model(), application) return list(application_config.keys()) def get_application_config_option(application, option): - """Return application configuration + """Return application configuration. :param application: Name of application :type application: string @@ -1134,7 +1101,6 @@ def get_application_config_option(application, option): :returns: Value of configuration option :rtype: Configuration option value type """ - application_config = model.get_application_config( lifecycle_utils.get_juju_model(), application) try: @@ -1144,13 +1110,11 @@ def get_application_config_option(application, option): def get_undercloud_auth(): - """Get the undercloud OpenStack authentication settings from the - environment. + """Get undercloud OpenStack authentication settings from environment. :returns: Dictionary of authentication settings :rtype: dict """ - os_auth_url = os.environ.get('OS_AUTH_URL') if os_auth_url: api_version = os_auth_url.split('/')[-1].replace('v', '') @@ -1213,6 +1177,11 @@ def get_undercloud_auth(): # Openstack Client helpers def get_keystone_ip(): + """Return the IP address to use when communicating with keystone api. + + :returns: IP address + :rtype: str + """ if get_application_config_option('keystone', 'vip'): return get_application_config_option('keystone', 'vip') unit = model.get_units( @@ -1221,13 +1190,11 @@ def get_keystone_ip(): def get_overcloud_auth(): - """Get the overcloud OpenStack authentication settings from the - environment. + """Get overcloud OpenStack authentication from the environment. :returns: Dictionary of authentication settings :rtype: dict """ - if get_application_config_option('keystone', 'use-https').lower() == 'yes': transport = 'https' port = 35357 @@ -1274,7 +1241,7 @@ def get_overcloud_auth(): def get_urllib_opener(): - """Create a urllib opener taking into account proxy settings + """Create a urllib opener taking into account proxy settings. Using urllib.request.urlopen will automatically handle proxies so none of this function is needed except we are currently specifying proxies @@ -1295,7 +1262,7 @@ def get_urllib_opener(): def find_cirros_image(arch): - """Return the url for the latest cirros image for the given architecture + """Return the url for the latest cirros image for the given architecture. :param arch: aarch64, arm, i386, x86_64 etc :type arch: str @@ -1310,7 +1277,7 @@ def find_cirros_image(arch): def download_image(image_url, target_file): - """Download the image from the given url to the specified file + """Download the image from the given url to the specified file. :param image_url: URL to download image from :type image_url: str @@ -1327,7 +1294,9 @@ def download_image(image_url, target_file): def resource_reaches_status(resource, resource_id, expected_status='available', msg='resource'): - """Wait for an openstack resources status to reach an expected status + """Wait for an openstack resources status to reach an expected status. + + Wait for an openstack resources status to reach an expected status within a specified time. Useful to confirm that nova instances, cinder vols, snapshots, glance images, heat stacks and other resources eventually reach the expected status. @@ -1351,7 +1320,7 @@ def resource_reaches_status(resource, resource_id, @tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60), reraise=True, stop=tenacity.stop_after_attempt(2)) def resource_removed(resource, resource_id, msg="resource"): - """Wait for an openstack resource to no longer be present + """Wait for an openstack resource to no longer be present. :param resource: pointer to os resource type, ex: heat_client.stacks :type resource: str @@ -1367,7 +1336,9 @@ def resource_removed(resource, resource_id, msg="resource"): def delete_resource(resource, resource_id, msg="resource"): - """Delete an openstack resource, such as one instance, keypair, + """Delete an openstack resource. + + Delete an openstack resource, such as one instance, keypair, image, volume, stack, etc., and confirm deletion within max wait time. :param resource: pointer to os resource type, ex:glance_client.images @@ -1384,7 +1355,7 @@ def delete_resource(resource, resource_id, msg="resource"): def delete_image(glance, img_id): - """Delete the given image from glance + """Delete the given image from glance. :param glance: Authenticated glanceclient :type glance: glanceclient.Client @@ -1396,7 +1367,7 @@ def delete_image(glance, img_id): def upload_image_to_glance(glance, local_path, image_name, disk_format='qcow2', visibility='public', container_format='bare'): - """Upload the given image to glance and apply the given label + """Upload the given image to glance and apply the given label. :param glance: Authenticated glanceclient :type glance: glanceclient.Client diff --git a/zaza/utilities/os_versions.py b/zaza/utilities/os_versions.py index c240c81..a6b0f29 100644 --- a/zaza/utilities/os_versions.py +++ b/zaza/utilities/os_versions.py @@ -1,3 +1,4 @@ +"""Module containing data about OpenStack versions.""" from collections import OrderedDict