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:
parent
700ab86f8d
commit
acdc2da0e3
@ -114,10 +114,6 @@ In this section we focus on this related to networking.
|
|||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
- **Cloudpipe**
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
- **Extended Networks**
|
- **Extended Networks**
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
@ -52,7 +52,6 @@ the `API guide <http://developer.openstack.org/api-guide/compute/index.html>`_.
|
|||||||
.. include:: os-services.inc
|
.. include:: os-services.inc
|
||||||
.. include:: os-simple-tenant-usage.inc
|
.. include:: os-simple-tenant-usage.inc
|
||||||
.. include:: os-server-external-events.inc
|
.. include:: os-server-external-events.inc
|
||||||
.. include:: os-cloudpipe.inc
|
|
||||||
.. include:: extensions.inc
|
.. include:: extensions.inc
|
||||||
.. include:: os-networks.inc
|
.. include:: os-networks.inc
|
||||||
.. include:: os-volumes.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.
|
Compute API in the past, but no longer exist.
|
||||||
|
|
||||||
.. include:: os-certificates.inc
|
.. include:: os-certificates.inc
|
||||||
|
.. include:: os-cloudpipe.inc
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
This API only works with ``nova-network`` which is
|
This API only works with ``nova-network`` which is
|
||||||
deprecated in favor of Neutron. It should be avoided
|
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.
|
Manages virtual VPNs for projects.
|
||||||
|
|
||||||
@ -106,4 +107,4 @@ Request
|
|||||||
Response
|
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
|
||||||
|
@ -14,26 +14,10 @@
|
|||||||
|
|
||||||
"""Connect your vlan to the world."""
|
"""Connect your vlan to the world."""
|
||||||
|
|
||||||
from oslo_utils import fileutils
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack.compute.schemas import cloudpipe
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
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'
|
ALIAS = 'os-cloudpipe'
|
||||||
|
|
||||||
@ -41,130 +25,23 @@ ALIAS = 'os-cloudpipe'
|
|||||||
class CloudpipeController(wsgi.Controller):
|
class CloudpipeController(wsgi.Controller):
|
||||||
"""Handle creating and listing cloudpipe instances."""
|
"""Handle creating and listing cloudpipe instances."""
|
||||||
|
|
||||||
def __init__(self):
|
@extensions.expected_errors((410))
|
||||||
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)
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
"""Create a new cloudpipe instance, if none exists.
|
"""Create a new cloudpipe instance, if none exists.
|
||||||
|
|
||||||
Parameters: {cloudpipe: {'project_id': ''}}
|
Parameters: {cloudpipe: {'project_id': ''}}
|
||||||
"""
|
"""
|
||||||
|
raise exc.HTTPGone()
|
||||||
|
|
||||||
context = req.environ['nova.context']
|
@extensions.expected_errors((410))
|
||||||
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))
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""List running cloudpipe instances."""
|
"""List running cloudpipe instances."""
|
||||||
context = req.environ['nova.context']
|
raise exc.HTTPGone()
|
||||||
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}
|
|
||||||
|
|
||||||
@wsgi.response(202)
|
@extensions.expected_errors(410)
|
||||||
@extensions.expected_errors(400)
|
|
||||||
@validation.schema(cloudpipe.update)
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
"""Configure cloudpipe parameters for the project."""
|
"""Configure cloudpipe parameters for the project."""
|
||||||
|
raise exc.HTTPGone()
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
class Cloudpipe(extensions.V21APIExtensionBase):
|
class Cloudpipe(extensions.V21APIExtensionBase):
|
||||||
|
@ -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,
|
|
||||||
}
|
|
@ -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.
|
|
||||||
"""
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -55,7 +55,6 @@ from six.moves import range
|
|||||||
|
|
||||||
from nova import block_device
|
from nova import block_device
|
||||||
from nova.cells import rpcapi as cells_rpcapi
|
from nova.cells import rpcapi as cells_rpcapi
|
||||||
from nova.cloudpipe import pipelib
|
|
||||||
from nova import compute
|
from nova import compute
|
||||||
from nova.compute import build_results
|
from nova.compute import build_results
|
||||||
from nova.compute import claims
|
from nova.compute import claims
|
||||||
@ -1460,7 +1459,7 @@ class ComputeManager(manager.Manager):
|
|||||||
instance.save(expected_task_state=[None])
|
instance.save(expected_task_state=[None])
|
||||||
self._update_resource_tracker(context, instance)
|
self._update_resource_tracker(context, instance)
|
||||||
|
|
||||||
is_vpn = pipelib.is_vpn_image(instance.image_ref)
|
is_vpn = False
|
||||||
return network_model.NetworkInfoAsyncWrapper(
|
return network_model.NetworkInfoAsyncWrapper(
|
||||||
self._allocate_network_async, context, instance,
|
self._allocate_network_async, context, instance,
|
||||||
requested_networks, macs, security_groups, is_vpn,
|
requested_networks, macs, security_groups, is_vpn,
|
||||||
|
@ -25,7 +25,6 @@ from nova.conf import base
|
|||||||
from nova.conf import cache
|
from nova.conf import cache
|
||||||
from nova.conf import cells
|
from nova.conf import cells
|
||||||
from nova.conf import cinder
|
from nova.conf import cinder
|
||||||
from nova.conf import cloudpipe
|
|
||||||
from nova.conf import compute
|
from nova.conf import compute
|
||||||
from nova.conf import conductor
|
from nova.conf import conductor
|
||||||
from nova.conf import configdrive
|
from nova.conf import configdrive
|
||||||
@ -80,7 +79,6 @@ base.register_opts(CONF)
|
|||||||
cache.register_opts(CONF)
|
cache.register_opts(CONF)
|
||||||
cells.register_opts(CONF)
|
cells.register_opts(CONF)
|
||||||
cinder.register_opts(CONF)
|
cinder.register_opts(CONF)
|
||||||
cloudpipe.register_opts(CONF)
|
|
||||||
compute.register_opts(CONF)
|
compute.register_opts(CONF)
|
||||||
conductor.register_opts(CONF)
|
conductor.register_opts(CONF)
|
||||||
configdrive.register_opts(CONF)
|
configdrive.register_opts(CONF)
|
||||||
|
@ -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}
|
|
@ -195,8 +195,9 @@ Related options:
|
|||||||
nova-network is deprecated, as are any related configuration options.
|
nova-network is deprecated, as are any related configuration options.
|
||||||
""",
|
""",
|
||||||
help="""
|
help="""
|
||||||
This is the public IP address for the cloudpipe VPN servers. It defaults to the
|
This option is no longer used since the /os-cloudpipe API was removed in the
|
||||||
IP address of the host.
|
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
|
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
|
Neutron in your deployment. It also will be ignored if the configuration option
|
||||||
|
@ -24,7 +24,6 @@ from nova.policies import baremetal_nodes
|
|||||||
from nova.policies import base
|
from nova.policies import base
|
||||||
from nova.policies import cells
|
from nova.policies import cells
|
||||||
from nova.policies import cells_scheduler
|
from nova.policies import cells_scheduler
|
||||||
from nova.policies import cloudpipe
|
|
||||||
from nova.policies import config_drive
|
from nova.policies import config_drive
|
||||||
from nova.policies import console_auth_tokens
|
from nova.policies import console_auth_tokens
|
||||||
from nova.policies import console_output
|
from nova.policies import console_output
|
||||||
@ -103,7 +102,6 @@ def list_rules():
|
|||||||
base.list_rules(),
|
base.list_rules(),
|
||||||
cells.list_rules(),
|
cells.list_rules(),
|
||||||
cells_scheduler.list_rules(),
|
cells_scheduler.list_rules(),
|
||||||
cloudpipe.list_rules(),
|
|
||||||
config_drive.list_rules(),
|
config_drive.list_rules(),
|
||||||
console_auth_tokens.list_rules(),
|
console_auth_tokens.list_rules(),
|
||||||
console_output.list_rules(),
|
console_output.list_rules(),
|
||||||
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"cloudpipe": {
|
|
||||||
"project_id": "%(project_id)s"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"instance_id": "%(id)s"
|
|
||||||
}
|
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"configure_project": {
|
|
||||||
"vpn_ip": "%(vpn_ip)s",
|
|
||||||
"vpn_port": "%(vpn_port)s"
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
@ -15,38 +15,14 @@
|
|||||||
|
|
||||||
import uuid as uuid_lib
|
import uuid as uuid_lib
|
||||||
|
|
||||||
import mock
|
|
||||||
from oslo_utils import timeutils
|
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova.api.openstack.compute import cloudpipe as cloudpipe_v21
|
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 import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
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)
|
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):
|
class CloudpipeTestV21(test.NoDBTestCase):
|
||||||
@ -58,141 +34,15 @@ class CloudpipeTestV21(test.NoDBTestCase):
|
|||||||
self.controller = self.cloudpipe.CloudpipeController()
|
self.controller = self.cloudpipe.CloudpipeController()
|
||||||
self.req = fakes.HTTPRequest.blank('')
|
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 test_cloudpipe_list(self):
|
||||||
|
self.assertRaises(exc.HTTPGone, self.controller.index, self.req)
|
||||||
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)
|
|
||||||
|
|
||||||
def test_cloudpipe_create(self):
|
def test_cloudpipe_create(self):
|
||||||
def _launch_vpn_instance(context):
|
body = {'cloudpipe': {'project_id': project_id}}
|
||||||
return ([fake_vpn_instance()], 'fake-reservation')
|
self.assertRaises(exc.HTTPGone, self.controller.create,
|
||||||
|
self.req, body=body)
|
||||||
|
|
||||||
with test.nested(
|
def test_cloudpipe_configure_project(self):
|
||||||
mock.patch.object(self.controller.compute_api, "get_all",
|
body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}}
|
||||||
return_value=[]),
|
self.assertRaises(exc.HTTPGone, self.controller.update,
|
||||||
mock.patch.object(self.controller.cloudpipe,
|
self.req, 'configure-project', body=body)
|
||||||
'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)
|
|
||||||
|
@ -15,74 +15,21 @@
|
|||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute import cloudpipe as clup_v21
|
from nova.api.openstack.compute import cloudpipe as clup_v21
|
||||||
from nova import exception
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
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):
|
class CloudpipeUpdateTestV21(test.NoDBTestCase):
|
||||||
bad_request = exception.ValidationError
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CloudpipeUpdateTestV21, self).setUp()
|
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.controller = clup_v21.CloudpipeController()
|
||||||
|
self.req = fakes.HTTPRequest.blank('')
|
||||||
|
|
||||||
def _check_status(self, expected_status, res, controller_method):
|
def _check_status(self, expected_status, res, controller_method):
|
||||||
self.assertEqual(expected_status, controller_method.wsgi_code)
|
self.assertEqual(expected_status, controller_method.wsgi_code)
|
||||||
|
|
||||||
def test_cloudpipe_configure_project(self):
|
def test_cloudpipe_configure_project(self):
|
||||||
body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}}
|
body = {"configure_project": {"vpn_ip": "1.2.3.4", "vpn_port": 222}}
|
||||||
result = self.controller.update(self.req, 'configure-project',
|
self.assertRaises(webob.exc.HTTPGone, self.controller.update,
|
||||||
body=body)
|
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)
|
|
||||||
|
@ -27,7 +27,6 @@ policy_data = """
|
|||||||
"os_compute_api:os-attach-interfaces": "",
|
"os_compute_api:os-attach-interfaces": "",
|
||||||
"os_compute_api:os-baremetal-nodes": "",
|
"os_compute_api:os-baremetal-nodes": "",
|
||||||
"os_compute_api:os-cells": "",
|
"os_compute_api:os-cells": "",
|
||||||
"os_compute_api:os-cloudpipe": "",
|
|
||||||
"os_compute_api:os-config-drive": "",
|
"os_compute_api:os-config-drive": "",
|
||||||
"os_compute_api:os-console-output": "",
|
"os_compute_api:os-console-output": "",
|
||||||
"os_compute_api:os-remote-consoles": "",
|
"os_compute_api:os-remote-consoles": "",
|
||||||
|
@ -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)
|
|
@ -291,7 +291,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||||||
"os_compute_api:os-cells:delete",
|
"os_compute_api:os-cells:delete",
|
||||||
"os_compute_api:os-cells:update",
|
"os_compute_api:os-cells:update",
|
||||||
"os_compute_api:os-cells:sync_instances",
|
"os_compute_api:os-cells:sync_instances",
|
||||||
"os_compute_api:os-cloudpipe",
|
|
||||||
"os_compute_api:os-evacuate",
|
"os_compute_api:os-evacuate",
|
||||||
"os_compute_api:os-extended-server-attributes",
|
"os_compute_api:os-extended-server-attributes",
|
||||||
"os_compute_api:os-fixed-ips",
|
"os_compute_api:os-fixed-ips",
|
||||||
|
@ -23,7 +23,6 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from nova.cloudpipe import pipelib
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.i18n import _LI
|
from nova.i18n import _LI
|
||||||
from nova.i18n import _LW
|
from nova.i18n import _LW
|
||||||
@ -190,9 +189,7 @@ class NWFilterFirewall(base_firewall.FirewallDriver):
|
|||||||
filters added to the list must also be correctly defined
|
filters added to the list must also be correctly defined
|
||||||
within the subclass.
|
within the subclass.
|
||||||
"""
|
"""
|
||||||
if pipelib.is_vpn_image(instance.image_ref):
|
if allow_dhcp:
|
||||||
base_filter = 'nova-vpn'
|
|
||||||
elif allow_dhcp:
|
|
||||||
base_filter = 'nova-base'
|
base_filter = 'nova-base'
|
||||||
else:
|
else:
|
||||||
base_filter = 'nova-nodhcp'
|
base_filter = 'nova-nodhcp'
|
||||||
@ -218,8 +215,6 @@ class NWFilterFirewall(base_firewall.FirewallDriver):
|
|||||||
self._define_filter(self._filter_container('nova-nodhcp', filter_set))
|
self._define_filter(self._filter_container('nova-nodhcp', filter_set))
|
||||||
filter_set.append('allow-dhcp-server')
|
filter_set.append('allow-dhcp-server')
|
||||||
self._define_filter(self._filter_container('nova-base', filter_set))
|
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._define_filter(self.nova_dhcp_filter())
|
||||||
|
|
||||||
self.static_filters_configured = True
|
self.static_filters_configured = True
|
||||||
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user