Implement /NetworkDriver.CreateNetwork

This patch replaces the mocked version of /NetworkDriver.CreateNetwork
with the actual Neutron call. The unit test for the endpoint is also
implemented.

Change-Id: I58517069470f1689d486e6a96a094f0dac959116
Signed-off-by: Taku Fukushima <f.tac.mac@gmail.com>
This commit is contained in:
Taku Fukushima 2015-08-03 19:11:19 +02:00
parent 50b9f430bc
commit 4b7dde26ea
5 changed files with 153 additions and 5 deletions

View File

@ -10,12 +10,24 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from flask import jsonify
from flask import request
from neutronclient.neutron import client
from kuryr import app
from kuryr.constants import SCHEMA
OS_URL = os.environ.get('OS_URL', 'http://127.0.0.1:9696/')
OS_TOKEN = os.environ.get('OS_TOKEN', '9999888877776666')
# TODO(tfukushima): Retrieve configuration info from a config file.
app.neutron = client.Client('2.0', endpoint_url=OS_URL, token=OS_TOKEN)
app.neutron.format = 'json'
@app.route('/Plugin.Activate', methods=['POST'])
def plugin_activate():
return jsonify(SCHEMA['PLUGIN_ACTIVATE'])
@ -23,6 +35,35 @@ def plugin_activate():
@app.route('/NetworkDriver.CreateNetwork', methods=['POST'])
def network_driver_create_network():
"""Creates a new Neutron Network which name is the given NetworkID.
This function takes the following JSON data and delegates the actual
network creation to the Neutron client. libnetwork's NetworkID is used as
the name of Network in Neutron. ::
{
"NetworkID": string,
"Options": {
...
}
}
See the following link for more details about the spec:
https://github.com/docker/libnetwork/blob/master/docs/remote.md#create-network # noqa
"""
json_data = request.get_json(force=True)
app.logger.debug("Received JSON data {0} for /NetworkDriver.CreateNetwork"
.format(json_data))
# TODO(tfukushima): Add a validation of the JSON data for the network.
neutron_network_name = json_data['NetworkID']
network = app.neutron.create_network(
{'network': {'name': neutron_network_name, "admin_state_up": True}})
app.logger.info("Created a new network with name {0} successfully: {1}"
.format(neutron_network_name, network))
return jsonify(SCHEMA['SUCCESS'])

View File

@ -10,12 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslotest import base
from neutronclient.tests.unit.test_cli20 import CLITestV20Base
from kuryr import app
class TestCase(base.BaseTestCase):
class TestCase(CLITestV20Base):
"""Test case base class for all unit tests."""
def setUp(self):
@ -23,6 +23,7 @@ class TestCase(base.BaseTestCase):
app.config['DEBUG'] = True
app.config['TESTING'] = True
self.app = app.test_client()
self.app.neutron = self.client
class TestKuryrBase(TestCase):
@ -30,9 +31,12 @@ class TestKuryrBase(TestCase):
def setUp(self):
super(TestKuryrBase, self).setUp()
self.app.neutron.format = 'json'
def tearDown(self):
super(TestKuryrBase, self).tearDown()
self.mox.VerifyAll()
self.mox.UnsetStubs()
class TestKuryrFailures(TestKuryrBase):

View File

@ -10,9 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import random
from ddt import ddt, data, unpack
from oslo_serialization import jsonutils
from kuryr import app
from kuryr.constants import SCHEMA
from kuryr.tests.base import TestKuryrBase
@ -36,7 +40,6 @@ class TestKuryr(TestKuryrBase):
- POST /NetworkDriver.Leave
"""
@data(('/Plugin.Activate', SCHEMA['PLUGIN_ACTIVATE']),
('/NetworkDriver.CreateNetwork', SCHEMA['SUCCESS']),
('/NetworkDriver.DeleteNetwork', SCHEMA['SUCCESS']),
('/NetworkDriver.CreateEndpoint', SCHEMA['CREATE_ENDPOINT']),
('/NetworkDriver.EndpointOperInfo', SCHEMA['ENDPOINT_OPER_INFO']),
@ -48,3 +51,41 @@ class TestKuryr(TestKuryrBase):
response = self.app.post(endpoint)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(expected, decoded_json)
def test_network_driver_create_network(self):
docker_network_id = hashlib.sha256(
str(random.getrandbits(256))).hexdigest()
self.mox.StubOutWithMock(app.neutron, "create_network")
fake_request = {
"network": {
"name": docker_network_id,
"admin_state_up": True
}
}
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa
fake_response = {
"network": {
"status": "ACTIVE",
"subnets": [],
"name": docker_network_id,
"admin_state_up": True,
"tenant_id": "9bacb3c5d39d41a79512987f338cf177",
"router:external": False,
"segments": [],
"shared": False,
"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c"
}
}
app.neutron.create_network(fake_request).AndReturn(fake_response)
self.mox.ReplayAll()
data = {'NetworkID': docker_network_id, 'Options': {}}
response = self.app.post('/NetworkDriver.CreateNetwork',
content_type='application/json',
data=jsonutils.dumps(data))
self.assertEqual(200, response.status_code)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(SCHEMA['SUCCESS'], decoded_json)

View File

@ -0,0 +1,61 @@
# 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 hashlib
import random
from neutronclient.common.exceptions import Unauthorized
from oslo_serialization import jsonutils
from kuryr import app
from kuryr.tests.base import TestKuryrFailures
class TestKuryrNetworkFailures(TestKuryrFailures):
"""Unittests for checking if Kuryr handles the failures for the networks.
This test covers error responses listed in the spec:
http://developer.openstack.org/api-ref-networking-v2-ext.html#createProviderNetwork # noqa
"""
def _create_network_with_exception(self, network_name, ex):
self.mox.StubOutWithMock(app.neutron, "create_network")
fake_bad_request_without_name = {
"network": {
"name": network_name,
"admin_state_up": True
}
}
app.neutron.create_network(
fake_bad_request_without_name).AndRaise(ex)
self.mox.ReplayAll()
def _invoke_create_request(self, network_name):
data = {'NetworkID': network_name, 'Options': {}}
response = self.app.post('/NetworkDriver.CreateNetwork',
content_type='application/json',
data=jsonutils.dumps(data))
return response
def test_create_network_unauthorized(self):
docker_network_id = hashlib.sha256(
str(random.getrandbits(256))).hexdigest()
self._create_network_with_exception(
docker_network_id, Unauthorized())
response = self._invoke_create_request(docker_network_id)
self.assertEqual(401, response.status_code)
decoded_json = jsonutils.loads(response.data)
self.assertTrue('Err' in decoded_json)
self.assertEqual(
{'Err': 'Unauthorized: bad credentials.'}, decoded_json)

View File

@ -36,8 +36,9 @@ def make_json_app(import_name, **kwargs):
@app.errorhandler(NeutronClientException)
def make_json_error(ex):
response = jsonify({"Err": str(ex)})
response.status_code = (ex.code
if isinstance(ex, HTTPException)
response.status_code = (ex.code if isinstance(ex, HTTPException)
else ex.status_code
if isinstance(ex, NeutronClientException)
else 500)
content_type = 'application/vnd.docker.plugins.v1+json; charset=utf-8'
response.headers['Content-Type'] = content_type