Adding Create Port Functionality for Cue Backend using TaskFlow
* Create port uses neutron client * Added unit tests for new functionality * Added a neutron test fixture Change-Id: Ia0f5d35d317726cd3ad22e4c5e1a34ae9eb65ddc
This commit is contained in:
parent
0b67215676
commit
35d8d42738
|
@ -110,6 +110,8 @@ class StubOutForTesting(object):
|
|||
class TestCase(base.BaseTestCase):
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
additional_fixtures = []
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test method to initialize test environment."""
|
||||
super(TestCase, self).setUp()
|
||||
|
@ -149,6 +151,12 @@ class TestCase(base.BaseTestCase):
|
|||
|
||||
self.session = db_api.get_session()
|
||||
|
||||
self._additional_fixtures = []
|
||||
for fixture_cls in self.additional_fixtures:
|
||||
fixture = fixture_cls()
|
||||
self.useFixture(fixture)
|
||||
self._additional_fixtures.append(fixture)
|
||||
|
||||
def tearDown(self):
|
||||
"""Runs after each test method to tear down test environment."""
|
||||
super(TestCase, self).tearDown()
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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
|
||||
|
||||
from cue import client
|
||||
from cue.tests import base
|
||||
import cue.tests.test_fixtures.neutron
|
||||
import os_tasklib.neutron as neutron_task
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
from taskflow import engines
|
||||
from taskflow.patterns import linear_flow
|
||||
|
||||
SHARED_CONF = {
|
||||
'connection': 'zookeeper',
|
||||
}
|
||||
|
||||
|
||||
class CreatePortTests(base.TestCase):
|
||||
|
||||
additional_fixtures = [
|
||||
cue.tests.test_fixtures.neutron.NeutronClient
|
||||
]
|
||||
|
||||
task_store = {
|
||||
'network_id': "0",
|
||||
'port_name': "port_0",
|
||||
}
|
||||
|
||||
def test_create_port_invalid_network(self):
|
||||
# retrieve neutron client API class
|
||||
neutron_client = client.neutron_client()
|
||||
|
||||
# create flow with "CreatePort" task
|
||||
flow = linear_flow.Flow('create port').add(neutron_task.CreatePort(
|
||||
os_client=neutron_client, provides='neutron_port_id'))
|
||||
|
||||
# generate a new UUID for an 'invalid' network_id
|
||||
CreatePortTests.task_store['network_id'] = str(uuid.uuid4())
|
||||
|
||||
try:
|
||||
engines.run(flow, store=CreatePortTests.task_store)
|
||||
except exceptions.NetworkNotFoundClient:
|
||||
"""this exception is expected for this test"""
|
||||
except Exception as e:
|
||||
self.fail("Unexpected exception was thrown: " + e.message)
|
||||
else:
|
||||
self.fail("NetworkNotFoundClient exception not thrown as expected")
|
||||
|
||||
def test_create_port(self):
|
||||
# retrieve neutron client API class
|
||||
neutron_client = client.neutron_client()
|
||||
|
||||
# retrieve networks
|
||||
#network_list = neutron_client.list_networks()
|
||||
#self.assertNotEqual(len(network_list['networks']), 0,
|
||||
# "No networks found")
|
||||
|
||||
# set a network_id and unique name to use
|
||||
#network_id = network_list['networks'][0]['id']
|
||||
network = neutron_client.create_network()
|
||||
network_id = network['network']['id']
|
||||
CreatePortTests.task_store['network_id'] = network_id
|
||||
CreatePortTests.task_store['port_name'] = "port_" + str(uuid.uuid4())
|
||||
|
||||
# create flow with "CreatePort" task, given neutron client
|
||||
flow = linear_flow.Flow('create port').add(neutron_task.CreatePort(
|
||||
os_client=neutron_client, provides='neutron_port_id'))
|
||||
|
||||
# execute flow with parameters required for "CreatePort" task
|
||||
engines.run(flow, store=CreatePortTests.task_store)
|
||||
|
||||
# retrieve list of ports from Neutron service
|
||||
port_list = neutron_client.list_ports()
|
||||
|
||||
# find our newly created port
|
||||
found = False
|
||||
for port in port_list['ports']:
|
||||
if port['network_id'] == CreatePortTests.task_store['network_id']:
|
||||
if port['name'] == CreatePortTests.task_store['port_name']:
|
||||
found = True
|
||||
break
|
||||
|
||||
self.assertEqual(True, found, "New port was not found")
|
|
@ -0,0 +1,58 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 fixtures
|
||||
import mock
|
||||
|
||||
|
||||
class BaseFixture(fixtures.Fixture):
|
||||
"""Base class for test fixtures
|
||||
|
||||
To create a test fixture, create a subclass of BaseFixture and define
|
||||
:meth:`setUp`
|
||||
|
||||
|
||||
Use :meth:`mock` to mock classes and map new methods to the mock'd class.
|
||||
"""
|
||||
def mock(self, cls):
|
||||
"""Convenience method for mock'ing classes.
|
||||
|
||||
The class specified by :param:`cls` will be mock'd and returned to the
|
||||
caller. Upon tearing down the fixture, the class will be reverted to
|
||||
its original representation.
|
||||
|
||||
To mock a class, pass in the string path to the class, and save the
|
||||
returned value to a variable. The mock'd version of the class that is
|
||||
returned will be used to redefine the class' behavior. ::
|
||||
|
||||
mocked_class = mock('package.SomeClass')
|
||||
|
||||
To replace a method in the mocked class, using a member method of the
|
||||
fixture allows the mock implementation to persist and share data
|
||||
between methods by using fixture member variables. ::
|
||||
|
||||
mocked_class.method1 = self.method1
|
||||
|
||||
See :meth:`cue.tests.test_fixtures.neutron.NeutronClient.setUp` for an
|
||||
example.
|
||||
|
||||
:param cls: Class to be mock'd. Pass in the string path to the class.
|
||||
:return: A mock'd version of the class.
|
||||
"""
|
||||
patch = mock.patch(cls)
|
||||
mock_instance = patch.__enter__()
|
||||
self.addCleanup(patch.__exit__)
|
||||
mocked_cls = mock_instance.return_value
|
||||
return mocked_cls
|
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
import neutronclient.neutron.client
|
||||
|
||||
import cue.tests.test_fixtures.base as base
|
||||
|
||||
|
||||
class NeutronClient(base.BaseFixture):
|
||||
"""A test fixture to simulate a Neutron Client connection
|
||||
|
||||
This class is used in test cases to simulate a real Neutron Client
|
||||
connection in the absence of a working Neutron API endpoint.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixture and apply all method overrides."""
|
||||
super(NeutronClient, self).setUp()
|
||||
|
||||
self._network_list = {}
|
||||
self._port_list = {}
|
||||
|
||||
v2_client = self.mock(neutronclient.neutron.client.API_VERSIONS['2.0'])
|
||||
v2_client.create_port = self.create_port
|
||||
v2_client.create_network = self.create_network
|
||||
v2_client.list_ports = self.list_ports
|
||||
|
||||
def create_port(self, body=None):
|
||||
"""Mock'd version of neutronclient...create_port().
|
||||
|
||||
Create a Neutron network port.
|
||||
|
||||
:param body: Dictionary with port information.
|
||||
:return: An updated copy of the 'body' that was passed in, with other
|
||||
information populated.
|
||||
"""
|
||||
if body and body['port'] and body['port']['network_id']:
|
||||
network_id = body['port']['network_id']
|
||||
if network_id not in self._network_list:
|
||||
raise exceptions.NetworkNotFoundClient(
|
||||
"network %s not found" % network_id
|
||||
)
|
||||
else:
|
||||
body = {'port': {}}
|
||||
|
||||
port_id = uuid.uuid4()
|
||||
body['port']['id'] = port_id
|
||||
self._port_list[port_id] = body['port']
|
||||
return body
|
||||
|
||||
def create_network(self, body=None):
|
||||
"""Mock'd version of neutronclient...create_network().
|
||||
|
||||
Create a Neutron network.
|
||||
|
||||
:param body: Dictionary with network information.
|
||||
:return: An updated copy of the 'body' that was passed in, with other
|
||||
information populated.
|
||||
"""
|
||||
if body and body['network']:
|
||||
pass
|
||||
else:
|
||||
body = {'network': {}}
|
||||
|
||||
network_id = uuid.uuid4()
|
||||
body['network'].update({
|
||||
'id': network_id,
|
||||
'subnets': [],
|
||||
'tenant_id': network_id,
|
||||
'segments': [],
|
||||
'shared': False,
|
||||
'port_security_enabled': True,
|
||||
})
|
||||
self._network_list[network_id] = body
|
||||
return body
|
||||
|
||||
def list_ports(self, retrieve_all=True, **_params):
|
||||
"""Mock'd version of neutronclient...list_ports().
|
||||
|
||||
List available ports.
|
||||
|
||||
:param retrieve_all: Set to true to retrieve all available ports
|
||||
"""
|
||||
if retrieve_all:
|
||||
return {'ports': self._port_list.values()}
|
|
@ -13,18 +13,28 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
__author__ = 'sputnik13'
|
||||
|
||||
import os_tasklib
|
||||
|
||||
|
||||
class CreatePort(os_tasklib.BaseTask):
|
||||
default_provides = 'neutron_port_id'
|
||||
|
||||
def execute(self, **kwargs):
|
||||
print("Create Neutron Port")
|
||||
return "RANDOM_NEUTRON_PORT_ID"
|
||||
def execute(self, network_id, port_name, **kwargs):
|
||||
body_value = {
|
||||
"port": {
|
||||
"admin_state_up": True,
|
||||
"name": port_name,
|
||||
"network_id": network_id,
|
||||
}
|
||||
}
|
||||
|
||||
port = self.os_client.create_port(body=body_value)
|
||||
port_id = port['port']['id']
|
||||
|
||||
return port_id
|
||||
|
||||
def revert(self, **kwargs):
|
||||
print("Delete Neutron Port %s" % kwargs['result'])
|
||||
"""Revert function for a failed create port task."""
|
||||
# TODO(dagnello): no action required for revert of a failed port create
|
||||
# task, but logging should be added with a flow transaction ID which
|
||||
# will provide context and state to the error.
|
||||
|
|
Loading…
Reference in New Issue