Remove cloudpipe APIs

This commit removes the cloudpipe API from nova. This has been
deprecated since change I415760ff634dd85974f0c3f79e788e633852efb5 and no
longer works without nova-cert and the pending removal of the deprecated
nova-network.

Implements bp remove-nova-cert
Change-Id: Ifd1fb13a5953cc66f9cc2561d30a9efcd3f4c92e
This commit is contained in:
Matthew Treinish 2017-04-24 19:43:54 -04:00 committed by He Jie Xu
parent 700ab86f8d
commit acdc2da0e3
27 changed files with 30 additions and 1033 deletions

View File

@ -114,10 +114,6 @@ In this section we focus on this related to networking.
TODO
- **Cloudpipe**
TODO
- **Extended Networks**
TODO

View File

@ -52,7 +52,6 @@ the `API guide <http://developer.openstack.org/api-guide/compute/index.html>`_.
.. 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

View File

@ -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
There is no body content for the response of a successful PUT request

View File

@ -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):

View File

@ -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,
}

View File

@ -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.
"""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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}

View File

@ -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

View File

@ -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(),

View File

@ -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

View File

@ -1,5 +0,0 @@
{
"cloudpipe": {
"project_id": "%(project_id)s"
}
}

View File

@ -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"
}
]
}

View File

@ -1,6 +0,0 @@
{
"configure_project": {
"vpn_ip": "%(vpn_ip)s",
"vpn_port": "%(vpn_port)s"
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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": "",

View File

@ -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)

View File

@ -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",

View File

@ -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

View File

@ -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.