Handle network is already created in DB

Change-Id: Iea68870add0bcf80e58ac7d7a93e3e87fcaefb03
Closes-Bug: #1797022
This commit is contained in:
Hongbin Lu 2018-10-10 03:22:31 +00:00
parent 580275b2a9
commit a156577f1b
9 changed files with 128 additions and 2 deletions

View File

@ -389,6 +389,11 @@ class NetworkNotFound(HTTPNotFound):
class NetworkAlreadyExists(ResourceExists):
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):
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)
@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")
def update_network(context, uuid, values):
"""Update properties of a network.

View File

@ -1250,6 +1250,19 @@ class Connection(object):
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):
# ensure defaults are present for new networks
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
# (and call docker.create_network later) if there are concurrent
# 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, "
"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)
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
def save(self, context=None):
"""Save updates to this Network.

View File

@ -10,8 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import uuidutils
import six
from zun.common import exception
import zun.conf
from zun.db import api as dbapi
from zun.tests.unit.db import base
from zun.tests.unit.db import utils
@ -36,3 +40,16 @@ class DbNetworkTestCase(base.DbTestCase):
'A network with neutron_net_id 456.*'):
utils.create_test_network(context=self.context,
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.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):
network_name = 'c02afe4e-8350-4263-8078'
self.network_api.remove_network(network_name)

View File

@ -15,6 +15,8 @@
import mock
from testtools.matchers import HasLength
from zun import objects
from zun.tests.unit.db import base
from zun.tests.unit.db import utils
@ -53,3 +55,13 @@ class TestNetworkObject(base.DbTestCase):
uuid,
params)
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',
'ContainerAction': '1.1-b0c721f9e10c6c0d1e41e512c49eb877',
'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a',
'Network': '1.1-f57547d39a95cf36f2b026aa4a863879',
'Network': '1.1-26e8d37a54e5fc905ede657744a221d9',
'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a',
}