diff --git a/api-guide/source/general_info.rst b/api-guide/source/general_info.rst index 0af94e6bcbed..da818ca4d16d 100644 --- a/api-guide/source/general_info.rst +++ b/api-guide/source/general_info.rst @@ -114,10 +114,6 @@ In this section we focus on this related to networking. TODO -- **Cloudpipe** - - TODO - - **Extended Networks** TODO diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst index 0b7a37bf8ba3..1fd4481aac21 100644 --- a/api-ref/source/index.rst +++ b/api-ref/source/index.rst @@ -52,7 +52,6 @@ the `API guide `_. .. include:: os-services.inc .. include:: os-simple-tenant-usage.inc .. include:: os-server-external-events.inc -.. include:: os-cloudpipe.inc .. include:: extensions.inc .. include:: os-networks.inc .. include:: os-volumes.inc @@ -79,3 +78,4 @@ This section contains the reference for APIs that were part of the OpenStack Compute API in the past, but no longer exist. .. include:: os-certificates.inc +.. include:: os-cloudpipe.inc diff --git a/api-ref/source/os-cloudpipe.inc b/api-ref/source/os-cloudpipe.inc index 2b102acea188..54af4f183eac 100644 --- a/api-ref/source/os-cloudpipe.inc +++ b/api-ref/source/os-cloudpipe.inc @@ -8,7 +8,8 @@ This API only works with ``nova-network`` which is deprecated in favor of Neutron. It should be avoided - in any new applications. + in any new applications. It was removed in the 16.0.0 + Pike release. Manages virtual VPNs for projects. @@ -106,4 +107,4 @@ Request Response -------- -There is no body content for the response of a successful PUT request \ No newline at end of file +There is no body content for the response of a successful PUT request diff --git a/nova/api/openstack/compute/cloudpipe.py b/nova/api/openstack/compute/cloudpipe.py index de7a5e523340..aff0482e6654 100644 --- a/nova/api/openstack/compute/cloudpipe.py +++ b/nova/api/openstack/compute/cloudpipe.py @@ -14,26 +14,10 @@ """Connect your vlan to the world.""" -from oslo_utils import fileutils from webob import exc -from nova.api.openstack.compute.schemas import cloudpipe from nova.api.openstack import extensions from nova.api.openstack import wsgi -from nova.api import validation -from nova.cloudpipe import pipelib -from nova import compute -from nova.compute import utils as compute_utils -from nova.compute import vm_states -import nova.conf -from nova import exception -from nova.i18n import _ -from nova import network -from nova import objects -from nova.policies import cloudpipe as cp_policies -from nova import utils - -CONF = nova.conf.CONF ALIAS = 'os-cloudpipe' @@ -41,130 +25,23 @@ ALIAS = 'os-cloudpipe' class CloudpipeController(wsgi.Controller): """Handle creating and listing cloudpipe instances.""" - def __init__(self): - self.compute_api = compute.API() - self.network_api = network.API() - self.cloudpipe = pipelib.CloudPipe() - self.setup() - - def setup(self): - """Ensure the keychains and folders exist.""" - # NOTE(vish): One of the drawbacks of doing this in the api is - # the keys will only be on the api node that launched - # the cloudpipe. - fileutils.ensure_tree(CONF.crypto.keys_path) - - def _get_all_cloudpipes(self, context): - """Get all cloudpipes.""" - instances = self.compute_api.get_all(context, - search_opts={'deleted': False}) - return [instance for instance in instances - if pipelib.is_vpn_image(instance.image_ref) - and instance.vm_state != vm_states.DELETED] - - def _get_cloudpipe_for_project(self, context): - """Get the cloudpipe instance for a project from context.""" - cloudpipes = self._get_all_cloudpipes(context) or [None] - return cloudpipes[0] - - def _vpn_dict(self, context, project_id, instance): - elevated = context.elevated() - rv = {'project_id': project_id} - if not instance: - rv['state'] = 'pending' - return rv - rv['instance_id'] = instance.uuid - rv['created_at'] = utils.isotime(instance.created_at) - nw_info = compute_utils.get_nw_info_for_instance(instance) - if not nw_info: - return rv - vif = nw_info[0] - ips = [ip for ip in vif.fixed_ips() if ip['version'] == 4] - if ips: - rv['internal_ip'] = ips[0]['address'] - # NOTE(vish): Currently network_api.get does an owner check on - # project_id. This is probably no longer necessary - # but rather than risk changes in the db layer, - # we are working around it here by changing the - # project_id in the context. This can be removed - # if we remove the project_id check in the db. - elevated.project_id = project_id - network = self.network_api.get(elevated, vif['network']['id']) - if network: - vpn_ip = network['vpn_public_address'] - vpn_port = network['vpn_public_port'] - rv['public_ip'] = vpn_ip - rv['public_port'] = vpn_port - if vpn_ip and vpn_port: - if utils.vpn_ping(vpn_ip, vpn_port): - rv['state'] = 'running' - else: - rv['state'] = 'down' - else: - rv['state'] = 'invalid' - return rv - - @extensions.expected_errors((400, 403)) - @validation.schema(cloudpipe.create) + @extensions.expected_errors((410)) def create(self, req, body): """Create a new cloudpipe instance, if none exists. Parameters: {cloudpipe: {'project_id': ''}} """ + raise exc.HTTPGone() - context = req.environ['nova.context'] - context.can(cp_policies.BASE_POLICY_NAME) - params = body.get('cloudpipe', {}) - project_id = params.get('project_id', context.project_id) - # NOTE(vish): downgrade to project context. Note that we keep - # the same token so we can still talk to glance - context.project_id = project_id - context.user_id = 'project-vpn' - context.is_admin = False - context.roles = [] - instance = self._get_cloudpipe_for_project(context) - if not instance: - try: - result = self.cloudpipe.launch_vpn_instance(context) - instance = result[0][0] - except exception.NoMoreNetworks: - msg = _("Unable to claim IP for VPN instances, ensure it " - "isn't running, and try again in a few minutes") - raise exc.HTTPBadRequest(explanation=msg) - return {'instance_id': instance.uuid} - - @extensions.expected_errors((400, 403, 404)) + @extensions.expected_errors((410)) def index(self, req): """List running cloudpipe instances.""" - context = req.environ['nova.context'] - context.can(cp_policies.BASE_POLICY_NAME) - vpns = [self._vpn_dict(context, x['project_id'], x) - for x in self._get_all_cloudpipes(context)] - return {'cloudpipes': vpns} + raise exc.HTTPGone() - @wsgi.response(202) - @extensions.expected_errors(400) - @validation.schema(cloudpipe.update) + @extensions.expected_errors(410) def update(self, req, id, body): """Configure cloudpipe parameters for the project.""" - - context = req.environ['nova.context'] - context.can(cp_policies.BASE_POLICY_NAME) - - if id != "configure-project": - msg = _("Unknown action %s") % id - raise exc.HTTPBadRequest(explanation=msg) - - project_id = context.project_id - networks = objects.NetworkList.get_by_project(context, project_id) - - params = body['configure_project'] - vpn_ip = params['vpn_ip'] - vpn_port = params['vpn_port'] - for nw in networks: - nw.vpn_public_address = vpn_ip - nw.vpn_public_port = vpn_port - nw.save() + raise exc.HTTPGone() class Cloudpipe(extensions.V21APIExtensionBase): diff --git a/nova/api/openstack/compute/schemas/cloudpipe.py b/nova/api/openstack/compute/schemas/cloudpipe.py deleted file mode 100644 index 43d72d0f4040..000000000000 --- a/nova/api/openstack/compute/schemas/cloudpipe.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2014 IBM Corporation. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -from nova.api.validation import parameter_types - - -create = { - 'type': 'object', - 'properties': { - 'cloudpipe': { - 'type': 'object', - 'properties': { - 'project_id': parameter_types.project_id, - }, - 'additionalProperties': False, - }, - }, - 'required': ['cloudpipe'], - 'additionalProperties': False, -} - -update = { - 'type': 'object', - 'properties': { - 'configure_project': { - 'type': 'object', - 'properties': { - 'vpn_ip': parameter_types.ip_address, - 'vpn_port': parameter_types.tcp_udp_port, - }, - 'required': ['vpn_ip', 'vpn_port'], - 'additionalProperties': False, - }, - }, - 'required': ['configure_project'], - 'additionalProperties': False, -} diff --git a/nova/cloudpipe/__init__.py b/nova/cloudpipe/__init__.py deleted file mode 100644 index aa6c77a5aa8c..000000000000 --- a/nova/cloudpipe/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -""" -:mod:`nova.cloudpipe` -- VPN Server Management -===================================================== - -.. automodule:: nova.cloudpipe - :platform: Unix - :synopsis: An OpenVPN server for every nova user. -""" diff --git a/nova/cloudpipe/bootscript.template b/nova/cloudpipe/bootscript.template deleted file mode 100755 index c6fbc08621e5..000000000000 --- a/nova/cloudpipe/bootscript.template +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -# This gets zipped and run on the cloudpipe-managed OpenVPN server - -export LC_ALL=C -export VPN_IP=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{print $$1}'` -export BROADCAST=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f3 | awk '{print $$1}'` -export DHCP_MASK=`ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f4 | awk '{print $$1}'` -export GATEWAY=`netstat -r | grep default | cut -d' ' -f10` -# Need a higher valued MAC address than eth0, to prevent the TAP MAC address -# from becoming the bridge MAC address. Since Essex eth0 MAC starts with -# FA:16:3E, we'll thus generate a MAC starting with FA:17:3E to be higher than eth0. -export RANDOM_TAP_MAC=`openssl rand -hex 8 | sed 's/\(..\)/\1:/g' | cut -b-8 | awk '{print "FA:17:3E:"$$1}'` - -DHCP_LOWER=`echo $$BROADCAST | awk -F. '{print $$1"."$$2"."$$3"." $$4 - ${num_vpn} }'` -DHCP_UPPER=`echo $$BROADCAST | awk -F. '{print $$1"."$$2"."$$3"." $$4 - 1 }'` - -# generate a server DH -openssl dhparam -out /etc/openvpn/dh1024.pem 1024 - -cp crl.pem /etc/openvpn/ -cp server.key /etc/openvpn/ -cp ca.crt /etc/openvpn/ -cp server.crt /etc/openvpn/ -# Customize the server.conf.template -cd /etc/openvpn - -sed -e s/VPN_IP/$$VPN_IP/g server.conf.template > server.conf -sed -i -e s/DHCP_SUBNET/$$DHCP_MASK/g server.conf -sed -i -e s/DHCP_LOWER/$$DHCP_LOWER/g server.conf -sed -i -e s/DHCP_UPPER/$$DHCP_UPPER/g server.conf -sed -i -e s/max-clients\ 1/max-clients\ 10/g server.conf - -echo "push \"route ${dmz_net} ${dmz_mask} $$GATEWAY\"" >> server.conf -echo "duplicate-cn" >> server.conf -echo "crl-verify /etc/openvpn/crl.pem" >> server.conf -echo "lladdr $$RANDOM_TAP_MAC" >> server.conf - -/etc/init.d/openvpn start diff --git a/nova/cloudpipe/client.ovpn.template b/nova/cloudpipe/client.ovpn.template deleted file mode 100644 index dd48b556f145..000000000000 --- a/nova/cloudpipe/client.ovpn.template +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -# NOVA user connection -# Edit the following lines to point to your cert files: -cert {{ certfile }} -key {{ keyfile }} - -ca cacert.pem - -client -dev tap -proto udp - -remote {{ ip }} {{ port }} -resolv-retry infinite -nobind - -# Downgrade privileges after initialization (non-Windows only) -user nobody -group nogroup -comp-lzo - -# Set log file verbosity. -verb 2 - -keepalive 10 120 -ping-timer-rem -persist-tun -persist-key diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py deleted file mode 100644 index 8cbb1c329001..000000000000 --- a/nova/cloudpipe/pipelib.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -""" -CloudPipe - Build a user-data payload zip file, and launch -an instance with it. - -""" - -import base64 -import os -import string -import zipfile - -from oslo_log import log as logging -from oslo_utils import fileutils - -from nova import compute -from nova.compute import flavors -import nova.conf -from nova import crypto -from nova import db -from nova import exception -from nova import utils - - -CONF = nova.conf.CONF - -LOG = logging.getLogger(__name__) - - -def is_vpn_image(image_id): - return image_id == CONF.cloudpipe.vpn_image_id - - -def _load_boot_script(): - with open(CONF.cloudpipe.boot_script_template, "r") as shellfile: - s = string.Template(shellfile.read()) - return s.substitute(dmz_net=CONF.cloudpipe.dmz_net, - dmz_mask=CONF.cloudpipe.dmz_mask, - num_vpn=CONF.cnt_vpn_clients) - - -class CloudPipe(object): - def __init__(self): - self.compute_api = compute.API() - - def get_encoded_zip(self, project_id): - # Make a payload.zip - with utils.tempdir() as tmpdir: - filename = "payload.zip" - zippath = os.path.join(tmpdir, filename) - z = zipfile.ZipFile(zippath, "w", zipfile.ZIP_DEFLATED) - boot_script = _load_boot_script() - # genvpn, sign csr - crypto.generate_vpn_files(project_id) - z.writestr('autorun.sh', boot_script) - crl = os.path.join(crypto.ca_folder(project_id), 'crl.pem') - z.write(crl, 'crl.pem') - server_key = os.path.join(crypto.ca_folder(project_id), - 'server.key') - z.write(server_key, 'server.key') - ca_crt = os.path.join(crypto.ca_path(project_id)) - z.write(ca_crt, 'ca.crt') - server_crt = os.path.join(crypto.ca_folder(project_id), - 'server.crt') - z.write(server_crt, 'server.crt') - z.close() - with open(zippath, "rb") as zippy: - # NOTE(vish): run instances expects encoded userdata, - # it is decoded in the get_metadata_call. - # autorun.sh also decodes the zip file, - # hence the double encoding. - encoded = base64.b64encode(zippy.read()) - encoded = base64.b64encode(encoded) - - return encoded - - def launch_vpn_instance(self, context): - LOG.debug("Launching VPN for %s", context.project_id) - key_name = self.setup_key_pair(context) - group_name = self.setup_security_group(context) - flavor = flavors.get_flavor_by_name(CONF.cloudpipe.vpn_flavor) - instance_name = '%s%s' % (context.project_id, - CONF.cloudpipe.vpn_key_suffix) - user_data = self.get_encoded_zip(context.project_id) - return self.compute_api.create(context, - flavor, - CONF.cloudpipe.vpn_image_id, - display_name=instance_name, - user_data=user_data, - key_name=key_name, - security_groups=[group_name]) - - def setup_security_group(self, context): - group_name = '%s%s' % (context.project_id, - CONF.cloudpipe.vpn_key_suffix) - group = {'user_id': context.user_id, - 'project_id': context.project_id, - 'name': group_name, - 'description': 'Group for vpn'} - try: - group_ref = db.security_group_create(context, group) - except exception.SecurityGroupExists: - return group_name - rule = {'parent_group_id': group_ref['id'], - 'cidr': '0.0.0.0/0', - 'protocol': 'udp', - 'from_port': 1194, - 'to_port': 1194} - db.security_group_rule_create(context, rule) - rule = {'parent_group_id': group_ref['id'], - 'cidr': '0.0.0.0/0', - 'protocol': 'icmp', - 'from_port': -1, - 'to_port': -1} - db.security_group_rule_create(context, rule) - # NOTE(vish): No need to trigger the group since the instance - # has not been run yet. - return group_name - - def setup_key_pair(self, context): - key_name = '%s%s' % (context.project_id, - CONF.cloudpipe.vpn_key_suffix) - try: - keypair_api = compute.api.KeypairAPI() - result, private_key = keypair_api.create_key_pair(context, - context.user_id, - key_name) - key_dir = os.path.join(CONF.crypto.keys_path, context.user_id) - fileutils.ensure_tree(key_dir) - key_path = os.path.join(key_dir, '%s.pem' % key_name) - with open(key_path, 'w') as f: - f.write(private_key) - except (exception.KeyPairExists, os.error, IOError): - pass - return key_name diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d0ac3c2983b6..c6263b932a75 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -55,7 +55,6 @@ from six.moves import range from nova import block_device from nova.cells import rpcapi as cells_rpcapi -from nova.cloudpipe import pipelib from nova import compute from nova.compute import build_results from nova.compute import claims @@ -1460,7 +1459,7 @@ class ComputeManager(manager.Manager): instance.save(expected_task_state=[None]) self._update_resource_tracker(context, instance) - is_vpn = pipelib.is_vpn_image(instance.image_ref) + is_vpn = False return network_model.NetworkInfoAsyncWrapper( self._allocate_network_async, context, instance, requested_networks, macs, security_groups, is_vpn, diff --git a/nova/conf/__init__.py b/nova/conf/__init__.py index 680fe1178835..8fb79336e245 100644 --- a/nova/conf/__init__.py +++ b/nova/conf/__init__.py @@ -25,7 +25,6 @@ from nova.conf import base from nova.conf import cache from nova.conf import cells from nova.conf import cinder -from nova.conf import cloudpipe from nova.conf import compute from nova.conf import conductor from nova.conf import configdrive @@ -80,7 +79,6 @@ base.register_opts(CONF) cache.register_opts(CONF) cells.register_opts(CONF) cinder.register_opts(CONF) -cloudpipe.register_opts(CONF) compute.register_opts(CONF) conductor.register_opts(CONF) configdrive.register_opts(CONF) diff --git a/nova/conf/cloudpipe.py b/nova/conf/cloudpipe.py deleted file mode 100644 index c58da8618606..000000000000 --- a/nova/conf/cloudpipe.py +++ /dev/null @@ -1,121 +0,0 @@ -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -from oslo_config import cfg - -from nova.conf import paths - -cloudpipe_group = cfg.OptGroup( - name='cloudpipe', - title='Cloudpipe options') - -cloudpipe_opts = [ - cfg.StrOpt('vpn_image_id', - default='0', - deprecated_group='DEFAULT', - help=""" -Image ID used when starting up a cloudpipe VPN client. - -An empty instance is created and configured with OpenVPN using -boot_script_template. This instance would be snapshotted and stored -in glance. ID of the stored image is used in 'vpn_image_id' to -create cloudpipe VPN client. - -Possible values: - -* Any valid ID of a VPN image -"""), - cfg.StrOpt('vpn_flavor', - default='m1.tiny', - deprecated_group='DEFAULT', - help=""" -Flavor for VPN instances. - -Possible values: - -* Any valid flavor name -"""), - cfg.StrOpt('boot_script_template', - default=paths.basedir_def('nova/cloudpipe/bootscript.template'), - deprecated_group='DEFAULT', - help=""" -Template for cloudpipe instance boot script. - -Possible values: - -* Any valid path to a cloudpipe instance boot script template - -Related options: - -The following options are required to configure cloudpipe-managed -OpenVPN server. - -* dmz_net -* dmz_mask -* cnt_vpn_clients -"""), - cfg.IPOpt('dmz_net', - default='10.0.0.0', - deprecated_group='DEFAULT', - help=""" -Network to push into OpenVPN config. - -Note: Above mentioned OpenVPN config can be found at -/etc/openvpn/server.conf. - -Possible values: - -* Any valid IPv4/IPV6 address - -Related options: - -* boot_script_template - dmz_net is pushed into bootscript.template - to configure cloudpipe-managed OpenVPN server -"""), - cfg.IPOpt('dmz_mask', - default='255.255.255.0', - deprecated_group='DEFAULT', - help=""" -Netmask to push into OpenVPN config. - -Possible values: - -* Any valid IPv4/IPV6 netmask - -Related options: - -* dmz_net - dmz_net and dmz_mask is pushed into bootscript.template - to configure cloudpipe-managed OpenVPN server -* boot_script_template -"""), - cfg.StrOpt('vpn_key_suffix', - default='-vpn', - deprecated_group='DEFAULT', - help=""" -Suffix to add to project name for VPN key and secgroups - -Possible values: - -* Any string value representing the VPN key suffix -""") -] - - -def register_opts(conf): - conf.register_group(cloudpipe_group) - conf.register_opts(cloudpipe_opts, group=cloudpipe_group) - - -def list_opts(): - return {cloudpipe_group: cloudpipe_opts} diff --git a/nova/conf/network.py b/nova/conf/network.py index 9fe244112b58..48ef77300fd0 100644 --- a/nova/conf/network.py +++ b/nova/conf/network.py @@ -195,8 +195,9 @@ Related options: nova-network is deprecated, as are any related configuration options. """, help=""" -This is the public IP address for the cloudpipe VPN servers. It defaults to the -IP address of the host. +This option is no longer used since the /os-cloudpipe API was removed in the +16.0.0 Pike release. This is the public IP address for the cloudpipe VPN +servers. It defaults to the IP address of the host. Please note that this option is only used when using nova-network instead of Neutron in your deployment. It also will be ignored if the configuration option diff --git a/nova/policies/__init__.py b/nova/policies/__init__.py index 08d012273ace..627b082b09fc 100644 --- a/nova/policies/__init__.py +++ b/nova/policies/__init__.py @@ -24,7 +24,6 @@ from nova.policies import baremetal_nodes from nova.policies import base from nova.policies import cells from nova.policies import cells_scheduler -from nova.policies import cloudpipe from nova.policies import config_drive from nova.policies import console_auth_tokens from nova.policies import console_output @@ -103,7 +102,6 @@ def list_rules(): base.list_rules(), cells.list_rules(), cells_scheduler.list_rules(), - cloudpipe.list_rules(), config_drive.list_rules(), console_auth_tokens.list_rules(), console_output.list_rules(), diff --git a/nova/policies/cloudpipe.py b/nova/policies/cloudpipe.py deleted file mode 100644 index 2951b83e69f9..000000000000 --- a/nova/policies/cloudpipe.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2016 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -from nova.policies import base - - -BASE_POLICY_NAME = 'os_compute_api:os-cloudpipe' - - -cloudpipe_policies = [ - base.create_rule_default( - BASE_POLICY_NAME, - base.RULE_ADMIN_API, - """List, create and update cloud pipes. - -os-cloudpipe API is deprecated as this works only with nova-network which \ -itself is deprecated. -""", - [ - { - 'method': 'GET', - 'path': '/os-cloudpipe' - }, - { - 'method': 'POST', - 'path': '/os-cloudpipe' - }, - { - 'method': 'PUT', - 'path': '/os-cloudpipe/configure-project' - } - ]), -] - - -def list_rules(): - return cloudpipe_policies diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-req.json.tpl deleted file mode 100644 index c8fc75995a95..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-req.json.tpl +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cloudpipe": { - "project_id": "%(project_id)s" - } -} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-resp.json.tpl deleted file mode 100644 index 6aa2ff60e2b2..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-create-resp.json.tpl +++ /dev/null @@ -1,3 +0,0 @@ -{ - "instance_id": "%(id)s" -} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-get-resp.json.tpl deleted file mode 100644 index 698008802e03..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-get-resp.json.tpl +++ /dev/null @@ -1,13 +0,0 @@ -{ - "cloudpipes": [ - { - "created_at": "%(isotime)s", - "instance_id": "%(uuid)s", - "internal_ip": "%(ip)s", - "project_id": "%(project_id)s", - "public_ip": "%(ip)s", - "public_port": 22, - "state": "down" - } - ] -} diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-update-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-update-req.json.tpl deleted file mode 100644 index 0ab9141aea01..000000000000 --- a/nova/tests/functional/api_sample_tests/api_samples/os-cloudpipe/cloud-pipe-update-req.json.tpl +++ /dev/null @@ -1,6 +0,0 @@ -{ - "configure_project": { - "vpn_ip": "%(vpn_ip)s", - "vpn_port": "%(vpn_port)s" - } -} diff --git a/nova/tests/functional/api_sample_tests/test_cloudpipe.py b/nova/tests/functional/api_sample_tests/test_cloudpipe.py deleted file mode 100644 index 5026095d70b0..000000000000 --- a/nova/tests/functional/api_sample_tests/test_cloudpipe.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -import uuid as uuid_lib - -import nova.conf -from nova.tests.functional.api_sample_tests import api_sample_base -from nova.tests.unit.image import fake - - -CONF = nova.conf.CONF - - -class CloudPipeSampleTest(api_sample_base.ApiSampleTestBaseV21): - ADMIN_API = True - sample_dir = "os-cloudpipe" - - def setUp(self): - super(CloudPipeSampleTest, self).setUp() - - def get_user_data(self, project_id): - """Stub method to generate user data for cloudpipe tests.""" - return "VVNFUiBEQVRB\n" - - def network_api_get(self, context, network_uuid): - """Stub to get a valid network and its information.""" - return {'vpn_public_address': '127.0.0.1', - 'vpn_public_port': 22} - - self.stub_out('nova.cloudpipe.pipelib.CloudPipe.get_encoded_zip', - get_user_data) - self.stub_out('nova.network.api.API.get', - network_api_get) - - def generalize_subs(self, subs, vanilla_regexes): - subs['project_id'] = '[0-9a-f-]+' - return subs - - def test_cloud_pipe_create(self): - # Get api samples of cloud pipe extension creation. - self.flags(vpn_image_id=fake.get_valid_image_id(), group='cloudpipe') - subs = {'project_id': str(uuid_lib.uuid4().hex)} - response = self._do_post('os-cloudpipe', 'cloud-pipe-create-req', - subs) - subs['image_id'] = CONF.cloudpipe.vpn_image_id - self._verify_response('cloud-pipe-create-resp', subs, response, 200) - return subs - - def test_cloud_pipe_list(self): - # Get api samples of cloud pipe extension get request. - subs = self.test_cloud_pipe_create() - response = self._do_get('os-cloudpipe') - subs['image_id'] = CONF.cloudpipe.vpn_image_id - self._verify_response('cloud-pipe-get-resp', subs, response, 200) - - def test_cloud_pipe_update(self): - subs = {'vpn_ip': '192.168.1.1', - 'vpn_port': '2000'} - response = self._do_put('os-cloudpipe/configure-project', - 'cloud-pipe-update-req', - subs) - self.assertEqual(202, response.status_code) - self.assertEqual("", response.text) diff --git a/nova/tests/unit/api/openstack/compute/test_cloudpipe.py b/nova/tests/unit/api/openstack/compute/test_cloudpipe.py index 0197bedba6fe..5fa5d4ea42e7 100644 --- a/nova/tests/unit/api/openstack/compute/test_cloudpipe.py +++ b/nova/tests/unit/api/openstack/compute/test_cloudpipe.py @@ -15,38 +15,14 @@ import uuid as uuid_lib -import mock -from oslo_utils import timeutils from webob import exc from nova.api.openstack.compute import cloudpipe as cloudpipe_v21 -from nova.compute import utils as compute_utils -import nova.conf -from nova import exception -from nova import objects from nova import test from nova.tests.unit.api.openstack import fakes -from nova.tests.unit import fake_network -from nova.tests.unit import matchers -from nova.tests import uuidsentinel as uuids -from nova import utils - -CONF = nova.conf.CONF project_id = str(uuid_lib.uuid4().hex) -uuid = uuids.fake - - -def fake_vpn_instance(): - return objects.Instance( - id=7, image_ref=CONF.cloudpipe.vpn_image_id, vm_state='active', - created_at=timeutils.parse_strtime('1981-10-20T00:00:00.000000'), - uuid=uuid, project_id=project_id) - - -def compute_api_get_all(context, search_opts=None): - return [fake_vpn_instance()] class CloudpipeTestV21(test.NoDBTestCase): @@ -58,141 +34,15 @@ class CloudpipeTestV21(test.NoDBTestCase): self.controller = self.cloudpipe.CloudpipeController() self.req = fakes.HTTPRequest.blank('') - def test_cloudpipe_list_no_network(self): - with test.nested( - mock.patch.object(compute_utils, 'get_nw_info_for_instance', - return_value={}), - mock.patch.object(self.controller.compute_api, "get_all", - side_effect=compute_api_get_all) - ) as (mock_utils_get_nw, mock_cpu_get_all): - res_dict = self.controller.index(self.req) - response = {'cloudpipes': [{'project_id': project_id, - 'instance_id': uuid, - 'created_at': '1981-10-20T00:00:00Z'}]} - - self.assertEqual(response, res_dict) - self.assertTrue(mock_cpu_get_all.called) - self.assertTrue(mock_utils_get_nw.called) - def test_cloudpipe_list(self): - - def network_api_get(context, network_id): - self.assertEqual(context.project_id, project_id) - return {'vpn_public_address': '127.0.0.1', - 'vpn_public_port': 22} - - def fake_get_nw_info_for_instance(instance): - return fake_network.fake_get_instance_nw_info(self) - - with test.nested( - mock.patch.object(utils, 'vpn_ping', return_value=True), - mock.patch.object(compute_utils, 'get_nw_info_for_instance', - side_effect=fake_get_nw_info_for_instance), - mock.patch.object(self.controller.network_api, "get", - side_effect=network_api_get), - mock.patch.object(self.controller.compute_api, "get_all", - side_effect=compute_api_get_all) - ) as (mock_vpn_ping, mock_utils_get, mock_nw_get, mock_cpu_get_all): - res_dict = self.controller.index(self.req) - response = {'cloudpipes': [{'project_id': project_id, - 'internal_ip': '192.168.1.100', - 'public_ip': '127.0.0.1', - 'public_port': 22, - 'state': 'running', - 'instance_id': uuid, - 'created_at': '1981-10-20T00:00:00Z'}]} - - self.assertThat(response, matchers.DictMatches(res_dict)) - self.assertTrue(mock_cpu_get_all.called) - self.assertTrue(mock_nw_get.called) - self.assertTrue(mock_utils_get.called) - self.assertTrue(mock_vpn_ping.called) + self.assertRaises(exc.HTTPGone, self.controller.index, self.req) def test_cloudpipe_create(self): - def _launch_vpn_instance(context): - return ([fake_vpn_instance()], 'fake-reservation') + body = {'cloudpipe': {'project_id': project_id}} + self.assertRaises(exc.HTTPGone, self.controller.create, + self.req, body=body) - with test.nested( - mock.patch.object(self.controller.compute_api, "get_all", - return_value=[]), - mock.patch.object(self.controller.cloudpipe, - 'launch_vpn_instance', - side_effect=_launch_vpn_instance), - ) as (mock_cpu_get_all, mock_vpn_launch): - body = {'cloudpipe': {'project_id': project_id}} - res_dict = self.controller.create(self.req, body=body) - response = {'instance_id': uuid} - - self.assertEqual(response, res_dict) - self.assertTrue(mock_cpu_get_all.called) - self.assertTrue(mock_vpn_launch.called) - - def test_cloudpipe_create_no_networks(self): - with test.nested( - mock.patch.object(self.controller.compute_api, "get_all", - return_value=[]), - mock.patch.object(self.controller.cloudpipe, - 'launch_vpn_instance', - side_effect=exception.NoMoreNetworks), - ) as (mock_cpu_get_all, mock_vpn_launch): - body = {'cloudpipe': {'project_id': project_id}} - req = fakes.HTTPRequest.blank(self.url) - - self.assertRaises(exc.HTTPBadRequest, - self.controller.create, req, body=body) - self.assertTrue(mock_cpu_get_all.called) - self.assertTrue(mock_vpn_launch.called) - - def test_cloudpipe_create_already_running(self): - with test.nested( - mock.patch.object(self.controller.cloudpipe, - 'launch_vpn_instance'), - mock.patch.object(self.controller.compute_api, "get_all", - side_effect=compute_api_get_all), - ) as (mock_vpn_launch, mock_cpu_get_all): - body = {'cloudpipe': {'project_id': project_id}} - req = fakes.HTTPRequest.blank(self.url) - res_dict = self.controller.create(req, body=body) - response = {'instance_id': uuid} - - self.assertEqual(response, res_dict) - # cloudpipe.launch_vpn_instance() should not be called - self.assertFalse(mock_vpn_launch.called) - self.assertTrue(mock_cpu_get_all.called) - - def test_cloudpipe_create_with_bad_project_id_failed(self): - body = {'cloudpipe': {'project_id': 'bad.project.id'}} - req = fakes.HTTPRequest.blank(self.url) - self.assertRaises(exception.ValidationError, - self.controller.create, req, body=body) - - -class CloudpipePolicyEnforcementV21(test.NoDBTestCase): - - def setUp(self): - super(CloudpipePolicyEnforcementV21, self).setUp() - self.controller = cloudpipe_v21.CloudpipeController() - self.req = fakes.HTTPRequest.blank('') - - def _common_policy_check(self, func, *arg, **kwarg): - rule_name = "os_compute_api:os-cloudpipe" - rule = {rule_name: "project:non_fake"} - self.policy.set_rules(rule) - exc = self.assertRaises( - exception.PolicyNotAuthorized, func, *arg, **kwarg) - self.assertEqual( - "Policy doesn't allow %s to be performed." % rule_name, - exc.format_message()) - - def test_list_policy_failed(self): - self._common_policy_check(self.controller.index, self.req) - - def test_create_policy_failed(self): - body = {'cloudpipe': {'project_id': uuid}} - self._common_policy_check(self.controller.create, self.req, body=body) - - def test_update_policy_failed(self): - body = {"configure_project": {'vpn_ip': '192.168.1.1', - 'vpn_port': 2000}} - self._common_policy_check( - self.controller.update, self.req, uuid, body=body) + def test_cloudpipe_configure_project(self): + body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}} + self.assertRaises(exc.HTTPGone, self.controller.update, + self.req, 'configure-project', body=body) diff --git a/nova/tests/unit/api/openstack/compute/test_cloudpipe_update.py b/nova/tests/unit/api/openstack/compute/test_cloudpipe_update.py index a49a7b15403f..64d9b13b0728 100644 --- a/nova/tests/unit/api/openstack/compute/test_cloudpipe_update.py +++ b/nova/tests/unit/api/openstack/compute/test_cloudpipe_update.py @@ -15,74 +15,21 @@ import webob from nova.api.openstack.compute import cloudpipe as clup_v21 -from nova import exception from nova import test from nova.tests.unit.api.openstack import fakes -from nova.tests.unit import fake_network - - -fake_networks = [fake_network.fake_network(1), - fake_network.fake_network(2)] - - -def fake_project_get_networks(context, project_id, associate=True): - return fake_networks - - -def fake_network_update(context, network_id, values): - for network in fake_networks: - if network['id'] == network_id: - for key in values: - network[key] = values[key] class CloudpipeUpdateTestV21(test.NoDBTestCase): - bad_request = exception.ValidationError def setUp(self): super(CloudpipeUpdateTestV21, self).setUp() - self.stub_out("nova.db.project_get_networks", - fake_project_get_networks) - self.stub_out("nova.db.network_update", fake_network_update) - self._setup() - self.req = fakes.HTTPRequest.blank('') - - def _setup(self): self.controller = clup_v21.CloudpipeController() + self.req = fakes.HTTPRequest.blank('') def _check_status(self, expected_status, res, controller_method): self.assertEqual(expected_status, controller_method.wsgi_code) def test_cloudpipe_configure_project(self): body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}} - result = self.controller.update(self.req, 'configure-project', - body=body) - self._check_status(202, result, self.controller.update) - self.assertEqual(fake_networks[0]['vpn_public_address'], "1.2.3.4") - self.assertEqual(fake_networks[0]['vpn_public_port'], 222) - - def test_cloudpipe_configure_project_bad_url(self): - body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}} - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.update, self.req, - 'configure-projectx', body=body) - - def test_cloudpipe_configure_project_bad_data(self): - body = {"configure_project": {"vpn_ipxx": "1.2.3.4", "vpn_port": 222}} - self.assertRaises(self.bad_request, - self.controller.update, self.req, - 'configure-project', body=body) - - def test_cloudpipe_configure_project_bad_vpn_port(self): - body = {"configure_project": {"vpn_ipxx": "1.2.3.4", - "vpn_port": "foo"}} - self.assertRaises(self.bad_request, - self.controller.update, self.req, - 'configure-project', body=body) - - def test_cloudpipe_configure_project_vpn_port_with_empty_string(self): - body = {"configure_project": {"vpn_ipxx": "1.2.3.4", - "vpn_port": ""}} - self.assertRaises(self.bad_request, - self.controller.update, self.req, - 'configure-project', body=body) + self.assertRaises(webob.exc.HTTPGone, self.controller.update, + self.req, 'configure-project', body=body) diff --git a/nova/tests/unit/fake_policy.py b/nova/tests/unit/fake_policy.py index 9fd8a6bbd603..11729fe82c5e 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -27,7 +27,6 @@ policy_data = """ "os_compute_api:os-attach-interfaces": "", "os_compute_api:os-baremetal-nodes": "", "os_compute_api:os-cells": "", - "os_compute_api:os-cloudpipe": "", "os_compute_api:os-config-drive": "", "os_compute_api:os-console-output": "", "os_compute_api:os-remote-consoles": "", diff --git a/nova/tests/unit/test_pipelib.py b/nova/tests/unit/test_pipelib.py deleted file mode 100644 index 6748d0d3e935..000000000000 --- a/nova/tests/unit/test_pipelib.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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. - -import mock -from oslo_config import cfg - -from nova.cloudpipe import pipelib -from nova import context -from nova import crypto -from nova import test -from nova import utils - -CONF = cfg.CONF - - -class PipelibTest(test.TestCase): - def setUp(self): - super(PipelibTest, self).setUp() - self.cloudpipe = pipelib.CloudPipe() - self.project = "222" - self.user = "111" - self.context = context.RequestContext(self.user, self.project) - - def test_get_encoded_zip(self): - with utils.tempdir() as tmpdir: - self.flags(ca_path=tmpdir, group='crypto') - crypto.ensure_ca_filesystem() - - ret = self.cloudpipe.get_encoded_zip(self.project) - self.assertTrue(ret) - - def test_launch_vpn_instance(self): - @mock.patch.object(self.cloudpipe.compute_api, 'create', - return_value=lambda *a, **kw: (None, "r-fakeres")) - def _do_test(mock_create): - with utils.tempdir() as tmpdir: - self.flags(ca_path=tmpdir, keys_path=tmpdir, group='crypto') - crypto.ensure_ca_filesystem() - self.cloudpipe.launch_vpn_instance(self.context) - - _do_test() - - def test_setup_security_group(self): - group_name = "%s%s" % (self.project, CONF.cloudpipe.vpn_key_suffix) - - # First attempt, does not exist (thus its created) - res1_group = self.cloudpipe.setup_security_group(self.context) - self.assertEqual(res1_group, group_name) - - # Second attempt, it exists in the DB - res2_group = self.cloudpipe.setup_security_group(self.context) - self.assertEqual(res1_group, res2_group) - - def test_setup_key_pair(self): - key_name = "%s%s" % (self.project, CONF.cloudpipe.vpn_key_suffix) - with utils.tempdir() as tmpdir: - self.flags(keys_path=tmpdir, group='crypto') - - # First attempt, key does not exist (thus it is generated) - res1_key = self.cloudpipe.setup_key_pair(self.context) - self.assertEqual(res1_key, key_name) - - # Second attempt, it exists in the DB - res2_key = self.cloudpipe.setup_key_pair(self.context) - self.assertEqual(res2_key, res1_key) diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index 238e818ff5a4..329a65908141 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -291,7 +291,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-cells:delete", "os_compute_api:os-cells:update", "os_compute_api:os-cells:sync_instances", -"os_compute_api:os-cloudpipe", "os_compute_api:os-evacuate", "os_compute_api:os-extended-server-attributes", "os_compute_api:os-fixed-ips", diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index cbfa3a5bc4f2..8157e0aa03ba 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -23,7 +23,6 @@ from oslo_log import log as logging from oslo_utils import excutils from oslo_utils import importutils -from nova.cloudpipe import pipelib import nova.conf from nova.i18n import _LI from nova.i18n import _LW @@ -190,9 +189,7 @@ class NWFilterFirewall(base_firewall.FirewallDriver): filters added to the list must also be correctly defined within the subclass. """ - if pipelib.is_vpn_image(instance.image_ref): - base_filter = 'nova-vpn' - elif allow_dhcp: + if allow_dhcp: base_filter = 'nova-base' else: base_filter = 'nova-nodhcp' @@ -218,8 +215,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver): self._define_filter(self._filter_container('nova-nodhcp', filter_set)) filter_set.append('allow-dhcp-server') self._define_filter(self._filter_container('nova-base', filter_set)) - self._define_filter(self._filter_container('nova-vpn', - ['allow-dhcp-server'])) self._define_filter(self.nova_dhcp_filter()) self.static_filters_configured = True diff --git a/releasenotes/notes/remove-cloudpipe-api-f7aea9372046ecfc.yaml b/releasenotes/notes/remove-cloudpipe-api-f7aea9372046ecfc.yaml new file mode 100644 index 000000000000..c26efca16a2c --- /dev/null +++ b/releasenotes/notes/remove-cloudpipe-api-f7aea9372046ecfc.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - The deprecated /os-cloudpipe API endpoint has been removed. Whenever calls + are made to that endpoint it now returns a 410 response.