Merge "Handle network is already created in DB"

This commit is contained in:
Zuul 2018-10-23 03:49:27 +00:00 committed by Gerrit Code Review
commit 8d431f596e
9 changed files with 128 additions and 2 deletions

View File

@ -389,6 +389,11 @@ class NetworkNotFound(HTTPNotFound):
class NetworkAlreadyExists(ResourceExists): class NetworkAlreadyExists(ResourceExists):
message = _("A network with %(field)s %(value)s already exists.") message = _("A network with %(field)s %(value)s already exists.")
def __init__(self, field, value):
self.field = field
self.value = value
super(NetworkAlreadyExists, self).__init__(field=field, value=value)
class PortNotFound(HTTPNotFound): class PortNotFound(HTTPNotFound):
message = _("Neutron port %(port)s could not be found.") message = _("Neutron port %(port)s could not be found.")

View File

@ -1081,6 +1081,28 @@ def get_network_by_uuid(context, network_uuid):
return _get_dbdriver_instance().get_network_by_uuid(context, network_uuid) return _get_dbdriver_instance().get_network_by_uuid(context, network_uuid)
@profiler.trace("db")
def list_networks(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""List matching networks.
Return a list of the specified columns for all networks that match
the specified filters.
:param context: The security context
:param filters: Filters to apply. Defaults to None.
:param limit: Maximum number of networks to return.
:param marker: the last item of the previous page; we return the next
result set.
:param sort_key: Attribute by which results should be sorted.
:param sort_dir: Direction in which results should be sorted.
(asc, desc)
:returns: A list of tuples of the specified columns.
"""
return _get_dbdriver_instance().list_networks(
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db") @profiler.trace("db")
def update_network(context, uuid, values): def update_network(context, uuid, values):
"""Update properties of a network. """Update properties of a network.

View File

@ -1250,6 +1250,19 @@ class Connection(object):
return result return result
def _add_networks_filters(self, query, filters):
filter_names = ['name', 'neutron_net_id', 'project_id', 'user_id']
return self._add_filters(query, models.Network, filters=filters,
filter_names=filter_names)
def list_networks(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
query = model_query(models.Network)
query = self._add_project_filters(context, query)
query = self._add_networks_filters(query, filters)
return _paginate_query(models.Network, limit, marker,
sort_key, sort_dir, query)
def create_network(self, context, values): def create_network(self, context, values):
# ensure defaults are present for new networks # ensure defaults are present for new networks
if not values.get('uuid'): if not values.get('uuid'):

View File

@ -118,7 +118,15 @@ class KuryrNetwork(network.Network):
# which will guarantee only one request can create the network in here # which will guarantee only one request can create the network in here
# (and call docker.create_network later) if there are concurrent # (and call docker.create_network later) if there are concurrent
# requests on creating networks for the same neutron net. # requests on creating networks for the same neutron net.
network.create(self.context) try:
network.create(self.context)
except exception.NetworkAlreadyExists as e:
if e.field == 'neutron_net_id':
network = objects.Network.list(
self.context,
filters={'neutron_net_id': network.neutron_net_id})[0]
else:
raise
LOG.debug("Calling docker.create_network to create network %s, " LOG.debug("Calling docker.create_network to create network %s, "
"ipam_options %s, options %s", name, ipam_options, options) "ipam_options %s, options %s", name, ipam_options, options)

View File

@ -75,6 +75,25 @@ class Network(base.ZunPersistentObject, base.ZunObject):
db_network = dbapi.create_network(context, values) db_network = dbapi.create_network(context, values)
self._from_db_object(self, db_network) self._from_db_object(self, db_network)
@base.remotable_classmethod
def list(cls, context, limit=None, marker=None,
sort_key=None, sort_dir=None, filters=None):
"""Return a list of Network objects.
:param context: Security context.
:param limit: maximum number of resources to return in a single result.
:param marker: pagination marker for large data sets.
:param sort_key: column to sort results by.
:param sort_dir: direction to sort. "asc" or "desc".
:param filters: filters when list networks.
:returns: a list of :class:`Network` object.
"""
db_networks = dbapi.list_networks(
context, limit=limit, marker=marker, sort_key=sort_key,
sort_dir=sort_dir, filters=filters)
return Network._from_db_object_list(db_networks, cls, context)
@base.remotable @base.remotable
def save(self, context=None): def save(self, context=None):
"""Save updates to this Network. """Save updates to this Network.

View File

@ -10,8 +10,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_utils import uuidutils
import six
from zun.common import exception from zun.common import exception
import zun.conf import zun.conf
from zun.db import api as dbapi
from zun.tests.unit.db import base from zun.tests.unit.db import base
from zun.tests.unit.db import utils from zun.tests.unit.db import utils
@ -36,3 +40,16 @@ class DbNetworkTestCase(base.DbTestCase):
'A network with neutron_net_id 456.*'): 'A network with neutron_net_id 456.*'):
utils.create_test_network(context=self.context, utils.create_test_network(context=self.context,
neutron_net_id='456') neutron_net_id='456')
def test_list_networks(self):
uuids = []
for i in range(1, 6):
network = utils.create_test_network(
uuid=uuidutils.generate_uuid(),
context=self.context,
neutron_net_id=uuidutils.generate_uuid(),
name='network' + str(i))
uuids.append(six.text_type(network['uuid']))
res = dbapi.list_networks(self.context)
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), sorted(res_uuids))

View File

@ -194,6 +194,36 @@ class KuryrNetworkTestCase(base.TestCase):
'neutron.net.shared': 'False', 'neutron.net.shared': 'False',
'neutron.subnet.uuid': 'fake-subnet-id'}) 'neutron.subnet.uuid': 'fake-subnet-id'})
@mock.patch.object(Network, 'create')
@mock.patch.object(Network, 'save')
@mock.patch.object(Network, 'list')
@mock.patch('zun.network.neutron.NeutronAPI')
def test_create_network_already_exist(
self, mock_neutron_api_cls, mock_list, mock_save, mock_create):
mock_neutron_api_cls.return_value = self.network_api.neutron_api
name = 'test_kuryr_network'
neutron_net_id = 'fake-net-id'
mock_create.side_effect = exception.NetworkAlreadyExists(
field='neutron_net_id', value=neutron_net_id)
with mock.patch.object(self.network_api.docker, 'create_network',
return_value={'Id': 'docker-net'}
) as mock_create_network:
network = self.network_api.create_network(name, neutron_net_id)
self.assertEqual('docker-net', network.network_id)
mock_list.assert_called_once_with(
self.context, filters={'neutron_net_id': neutron_net_id})
mock_create_network.assert_called_once_with(
name=name,
driver='kuryr',
enable_ipv6=False,
ipam={'Config': [{'Subnet': '10.5.0.0/16', 'Gateway': '10.5.0.1'}],
'Driver': 'kuryr',
'Options': {'neutron.net.shared': 'False',
'neutron.subnet.uuid': 'fake-subnet-id'}},
options={'neutron.net.uuid': 'fake-net-id',
'neutron.net.shared': 'False',
'neutron.subnet.uuid': 'fake-subnet-id'})
def test_remove_network(self): def test_remove_network(self):
network_name = 'c02afe4e-8350-4263-8078' network_name = 'c02afe4e-8350-4263-8078'
self.network_api.remove_network(network_name) self.network_api.remove_network(network_name)

View File

@ -15,6 +15,8 @@
import mock import mock
from testtools.matchers import HasLength
from zun import objects from zun import objects
from zun.tests.unit.db import base from zun.tests.unit.db import base
from zun.tests.unit.db import utils from zun.tests.unit.db import utils
@ -53,3 +55,13 @@ class TestNetworkObject(base.DbTestCase):
uuid, uuid,
params) params)
self.assertEqual(self.context, network._context) self.assertEqual(self.context, network._context)
def test_list(self):
with mock.patch.object(self.dbapi, 'list_networks',
autospec=True) as mock_get_list:
mock_get_list.return_value = [self.fake_network]
networks = objects.Network.list(self.context)
self.assertEqual(1, mock_get_list.call_count)
self.assertThat(networks, HasLength(1))
self.assertIsInstance(networks[0], objects.Network)
self.assertEqual(self.context, networks[0]._context)

View File

@ -365,7 +365,7 @@ object_data = {
'ContainerPCIRequests': '1.0-7b8f7f044661fe4e24e6949c035af2c4', 'ContainerPCIRequests': '1.0-7b8f7f044661fe4e24e6949c035af2c4',
'ContainerAction': '1.1-b0c721f9e10c6c0d1e41e512c49eb877', 'ContainerAction': '1.1-b0c721f9e10c6c0d1e41e512c49eb877',
'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a', 'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a',
'Network': '1.1-f57547d39a95cf36f2b026aa4a863879', 'Network': '1.1-26e8d37a54e5fc905ede657744a221d9',
'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a', 'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a',
} }