From b4fb2c404c371a5ba9b28a3ac769c94e69b8c141 Mon Sep 17 00:00:00 2001 From: Jeremy Freudberg Date: Mon, 22 May 2017 11:32:59 -0400 Subject: [PATCH] Use neutronclient for all network operations Latest novaclient 8.0.0 removed nova-network related functionality which Sahara depended on for network operations, even for Neutron. Now, use neutronclient (and Neutron resources in Heat) for all network operations. Additionally, fix devstack coordination backend. Partially-Implements: bp sahara-remove-nova-network Closes-Bug: #1692551 Co-Authored-By: Vitaly Gridnev Change-Id: I6b8e12f0d33752e7790c5f40ebe6a3940ff1b0c1 --- bindep.txt | 3 ++ devstack/plugin.sh | 4 +- devstack/settings | 6 +-- ...nova-network-removal-debe306fd7c61268.yaml | 8 +++ sahara/service/engine.py | 11 ++-- sahara/service/heat/templates.py | 50 ++++++++++++------- sahara/service/networks.py | 14 ------ sahara/service/validations/base.py | 13 ++--- sahara/tests/unit/service/validation/utils.py | 32 ++++++------ sahara/utils/openstack/neutron.py | 9 ++++ sahara/utils/openstack/nova.py | 8 --- 11 files changed, 81 insertions(+), 77 deletions(-) create mode 100644 releasenotes/notes/nova-network-removal-debe306fd7c61268.yaml diff --git a/bindep.txt b/bindep.txt index 19c65109..baf414df 100644 --- a/bindep.txt +++ b/bindep.txt @@ -1,6 +1,9 @@ # This file contains runtime (non-python) dependencies # More info at: http://docs.openstack.org/infra/bindep/readme.html +libssl-dev [platform:dpkg] +openssl-devel [platform:rpm] + # Define the basic (test) requirements extracted from bindata-fallback.txt # - mysqladmin and psql mariadb [platform:rpm] diff --git a/devstack/plugin.sh b/devstack/plugin.sh index d11a1ead..4989eb0c 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -185,7 +185,9 @@ function configure_sahara { # Enable distributed periodic tasks iniset $SAHARA_CONF_FILE DEFAULT periodic_coordinator_backend_url\ $SAHARA_PERIODIC_COORDINATOR_URL - pip_install tooz[zookeeper] + pip_install tooz[memcached] + + restart_service memcached fi recreate_database sahara diff --git a/devstack/settings b/devstack/settings index 39625362..646bdfac 100644 --- a/devstack/settings +++ b/devstack/settings @@ -34,7 +34,7 @@ SAHARA_BIN_DIR=$(get_python_exec_prefix) SAHARA_ENABLE_DISTRIBUTED_PERIODICS=${SAHARA_ENABLE_DISTRIBUTED_PERIODICS:-\ True} SAHARA_PERIODIC_COORDINATOR_URL=${SAHARA_PERIODIC_COORDINATOR_URL:-\ -kazoo://127.0.0.1:2181} +memcached://127.0.0.1:11211} #Toggle for deploying Sahara API with Apache + mod_wsgi SAHARA_USE_MOD_WSGI=${SAHARA_USE_MOD_WSGI:-False} @@ -44,7 +44,3 @@ TEMPEST_SERVICES+=,sahara enable_service sahara-api sahara-eng enable_service heat h-eng h-api h-api-cfn h-api-cw - -if [ "$SAHARA_ENABLE_DISTRIBUTED_PERIODICS" == "True" ]; then - enable_service zookeeper -fi diff --git a/releasenotes/notes/nova-network-removal-debe306fd7c61268.yaml b/releasenotes/notes/nova-network-removal-debe306fd7c61268.yaml new file mode 100644 index 00000000..2d57230e --- /dev/null +++ b/releasenotes/notes/nova-network-removal-debe306fd7c61268.yaml @@ -0,0 +1,8 @@ +--- +issues: + - Ironic integration might be broken if floating IPs are used, due to the + use of pre-created ports by the Sahara engine. The status of Ironic + support was untested for this release. +deprecations: + - Support for nova-network is removed, reflective of its removal from nova + itself and from python-novaclient. use_neutron=False is unsupported. diff --git a/sahara/service/engine.py b/sahara/service/engine.py index 7e17194b..654e5844 100644 --- a/sahara/service/engine.py +++ b/sahara/service/engine.py @@ -272,15 +272,10 @@ sed '/^Defaults requiretty*/ s/^/#/' -i /etc/sudoers\n server_groups_old[0].id) def _shutdown_instance(self, instance): - # tmckay-fp perfect, already testing the right thing + # Heat dissociates and deletes upon deletion of resources + # See OS::Neutron::FloatingIP and OS::Neutron::FloatingIPAssociation if instance.node_group.floating_ip_pool: - try: - b.execute_with_retries(networks.delete_floating_ip, - instance.instance_id) - except nova_exceptions.NotFound: - LOG.warning("Attempted to delete non-existent floating IP " - "in pool {pool} from instance" - .format(pool=instance.node_group.floating_ip_pool)) + pass try: volumes.detach_from_instance(instance) diff --git a/sahara/service/heat/templates.py b/sahara/service/heat/templates.py index 832ccc09..45ae776f 100644 --- a/sahara/service/heat/templates.py +++ b/sahara/service/heat/templates.py @@ -454,21 +454,25 @@ class ClusterStack(object): properties = {} inst_name = _get_inst_name(ng) - - if ng.floating_ip_pool: - resources.update(self._serialize_nova_floating(ng)) - - if CONF.use_neutron: - properties["networks"] = [{ - "network": self.cluster.neutron_management_network}] + private_net = self.cluster.neutron_management_network if ng.security_groups or ng.auto_security_group: - properties["security_groups"] = self._get_security_groups(ng) + sec_groups = self._get_security_groups(ng) # Check if cluster contains user key-pair and include it to template. if self.cluster.user_keypair_id: properties["key_name"] = self.cluster.user_keypair_id + port_name = _get_port_name(ng) + + resources.update(self._serialize_port( + port_name, private_net, sec_groups)) + + properties["networks"] = [{"port": {"get_resource": "port"}}] + + if ng.floating_ip_pool: + resources.update(self._serialize_neutron_floating(ng)) + gen_userdata_func = self.node_groups_extra[ng.id]['gen_userdata_func'] key_script = gen_userdata_func(ng, inst_name) if CONF.heat_enable_wait_condition: @@ -537,23 +541,33 @@ class ClusterStack(object): } } - def _serialize_nova_floating(self, ng): + def _serialize_neutron_floating(self, ng): return { "floating_ip": { - "type": "OS::Nova::FloatingIP", + "type": "OS::Neutron::FloatingIP", "properties": { - "pool": ng.floating_ip_pool - } - }, - "floating_ip_assoc": { - "type": "OS::Nova::FloatingIPAssociation", - "properties": { - "floating_ip": {"get_resource": "floating_ip"}, - "server_id": {"get_resource": INSTANCE_RESOURCE_NAME} + "floating_network_id": ng.floating_ip_pool, + "port_id": {"get_resource": "port"} } } } + def _serialize_port(self, port_name, fixed_net_id, security_groups): + properties = { + "network_id": fixed_net_id, + "replacement_policy": "AUTO", + "name": port_name + } + if security_groups: + properties["security_groups"] = security_groups + + return { + "port": { + "type": "OS::Neutron::Port", + "properties": properties, + } + } + def _serialize_volume(self, ng): if not ng.volumes_size or not ng.volumes_per_node: return {} diff --git a/sahara/service/networks.py b/sahara/service/networks.py index eef06a84..91c59c63 100644 --- a/sahara/service/networks.py +++ b/sahara/service/networks.py @@ -18,7 +18,6 @@ import six from sahara import conductor as c from sahara import context -from sahara.utils.openstack import base as b from sahara.utils.openstack import nova conductor = c.API @@ -65,16 +64,3 @@ def init_instances_ips(instance): "internal_ip": internal_ip}) return internal_ip and management_ip - - -def assign_floating_ip(instance_id, pool): - ip = b.execute_with_retries(nova.client().floating_ips.create, pool) - server = b.execute_with_retries(nova.client().servers.get, instance_id) - b.execute_with_retries(server.add_floating_ip, ip) - - -def delete_floating_ip(instance_id): - fl_ips = b.execute_with_retries( - nova.client().floating_ips.findall, instance_id=instance_id) - for fl_ip in fl_ips: - b.execute_with_retries(nova.client().floating_ips.delete, fl_ip.id) diff --git a/sahara/service/validations/base.py b/sahara/service/validations/base.py index cab5f45b..064f5e44 100644 --- a/sahara/service/validations/base.py +++ b/sahara/service/validations/base.py @@ -28,6 +28,7 @@ from sahara.service.api import v10 as api from sahara.utils import general as g import sahara.utils.openstack.cinder as cinder from sahara.utils.openstack import images as sahara_images +import sahara.utils.openstack.neutron as neutron import sahara.utils.openstack.nova as nova @@ -181,11 +182,11 @@ def check_flavor_exists(flavor_id): def check_security_groups_exist(security_groups): - security_group_list = nova.client().security_groups.list() + security_group_list = neutron.client().list_security_groups() allowed_groups = set() - for sg in security_group_list: - allowed_groups.add(six.text_type(sg.id)) - allowed_groups.add(sg.name) + for sg in security_group_list['security_groups']: + allowed_groups.add(sg['name']) + allowed_groups.add(sg['id']) for sg in security_groups: if sg not in allowed_groups: @@ -196,7 +197,7 @@ def check_security_groups_exist(security_groups): def check_floatingip_pool_exists(ng_name, pool_id): network = None if CONF.use_neutron: - network = nova.get_network(id=pool_id) + network = neutron.get_network(pool_id) else: # tmckay-fp, whoa, this suggests that we allow floating_ip_pools with # nova? Can that be true? Scour for this @@ -292,7 +293,7 @@ def check_keypair_exists(keypair): def check_network_exists(net_id): - if not nova.get_network(id=net_id): + if not neutron.get_network(net_id): raise ex.NotFoundException(net_id, _("Network %s not found")) diff --git a/sahara/tests/unit/service/validation/utils.py b/sahara/tests/unit/service/validation/utils.py index 4193deba..7862c2a3 100644 --- a/sahara/tests/unit/service/validation/utils.py +++ b/sahara/tests/unit/service/validation/utils.py @@ -17,6 +17,7 @@ import ast import re import mock +from neutronclient.common import exceptions as neutron_ex import novaclient.exceptions as nova_ex import six @@ -62,10 +63,9 @@ def _get_keypair(name): raise nova_ex.NotFound("") -def _get_network(**kwargs): - if 'id' in kwargs and ( - kwargs['id'] != "d9a3bebc-f788-4b81-9a93-aa048022c1ca"): - raise nova_ex.NotFound("") +def _get_network(resource, id): + if id != "d9a3bebc-f788-4b81-9a93-aa048022c1ca": + raise neutron_ex.NotFound("") return 'OK' @@ -104,20 +104,15 @@ class FakeFlavor(object): self.id = id -class FakeSecurityGroup(object): - def __init__(self, id, name): - self.id = id - self.name = name - - def _get_flavors_list(): return [FakeFlavor("42")] def _get_security_groups_list(): - return [FakeSecurityGroup("1", "default"), - FakeSecurityGroup("2", "group1"), - FakeSecurityGroup("3", "group2")] + return {'security_groups': [ + {"id": "1", "name": "default"}, + {"id": "2", "name": "group1"}, + {"id": "3", "name": "group2"}]} def start_patch(patch_templates=True): @@ -158,13 +153,16 @@ def start_patch(patch_templates=True): get_cl_templates.return_value = [] nova().flavors.list.side_effect = _get_flavors_list - nova().security_groups.list.side_effect = _get_security_groups_list nova().keypairs.get.side_effect = _get_keypair - nova().networks.find.side_effect = _get_network - nova().networks.find.__name__ = 'find' nova().floating_ip_pools.list.side_effect = _get_fl_ip_pool_list nova().availability_zones.list.side_effect = _get_availability_zone_list + neutron_p = mock.patch("sahara.utils.openstack.neutron.client") + neutron = neutron_p.start() + neutron().find_resource_by_id.side_effect = _get_network + neutron().find_resource_by_id.__name__ = 'find_resource_by_id' + neutron().list_security_groups.side_effect = _get_security_groups_list + heat = heat_p.start() heat().stacks.list.side_effect = _get_heat_stack_list @@ -235,7 +233,7 @@ def start_patch(patch_templates=True): # request data to validate patchers = [get_clusters_p, get_cluster_p, nova_p, get_image_p, heat_p, image_manager_p, cinder_p, - cinder_exists_p] + cinder_exists_p, neutron_p] if patch_templates: patchers.extend([get_ng_template_p, get_ng_templates_p, get_cl_template_p, get_cl_templates_p]) diff --git a/sahara/utils/openstack/neutron.py b/sahara/utils/openstack/neutron.py index b916e798..799169e6 100644 --- a/sahara/utils/openstack/neutron.py +++ b/sahara/utils/openstack/neutron.py @@ -14,6 +14,7 @@ # limitations under the License. +from neutronclient.common import exceptions as n_ex from neutronclient.neutron import client as neutron_cli from oslo_config import cfg from oslo_log import log as logging @@ -104,3 +105,11 @@ def get_private_network_cidrs(cluster): cidrs.append(subnet['subnet']['cidr']) return cidrs + + +def get_network(id): + try: + return base.execute_with_retries( + client().find_resource_by_id, 'network', id) + except n_ex.NotFound: + return None diff --git a/sahara/utils/openstack/nova.py b/sahara/utils/openstack/nova.py index db12656c..f6dab1e1 100644 --- a/sahara/utils/openstack/nova.py +++ b/sahara/utils/openstack/nova.py @@ -14,7 +14,6 @@ # limitations under the License. from novaclient import client as nova_client -from novaclient import exceptions as nova_ex from oslo_config import cfg from sahara.service import sessions @@ -57,10 +56,3 @@ def get_flavor(**kwargs): def get_instance_info(instance): return base.execute_with_retries( client().servers.get, instance.instance_id) - - -def get_network(**kwargs): - try: - return base.execute_with_retries(client().networks.find, **kwargs) - except nova_ex.NotFound: - return None