Merge "Add attach a network to a container server side."

This commit is contained in:
Jenkins 2017-09-14 02:59:05 +00:00 committed by Gerrit Code Review
commit fe28a063f3
15 changed files with 139 additions and 6 deletions

View File

@ -774,3 +774,38 @@ This request does not return anything in the response body.
.. rest_parameters:: parameters.yaml .. rest_parameters:: parameters.yaml
- X-Openstack-Request-Id: request_id - X-Openstack-Request-Id: request_id
Attach a network to a container
===============================
.. rest_method:: POST /v1/containers/{container_ident}/network_attach?network={network}
Attach a network to a container.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 202
.. rest_status_code:: error status.yaml
- 401
- 403
- 404
.. rest_parameters:: parameters.yaml
- container_ident: container_ident
- network: network
Response
--------
This request does not return anything in the response body.
.. rest_parameters:: parameters.yaml
- X-Openstack-Request-Id: request_id

View File

@ -32,6 +32,7 @@
"container:commit": "rule:default", "container:commit": "rule:default",
"container:add_security_group": "rule:default", "container:add_security_group": "rule:default",
"container:network_detach": "rule:default", "container:network_detach": "rule:default",
"container:network_attach": "rule:default",
"image:pull": "rule:default", "image:pull": "rule:default",
"image:get_all": "rule:default", "image:get_all": "rule:default",

View File

@ -114,7 +114,8 @@ class ContainersController(base.Controller):
'stats': ['GET'], 'stats': ['GET'],
'commit': ['POST'], 'commit': ['POST'],
'add_security_group': ['POST'], 'add_security_group': ['POST'],
'network_detach': ['POST'] 'network_detach': ['POST'],
'network_attach': ['POST']
} }
@pecan.expose('json') @pecan.expose('json')
@ -765,3 +766,17 @@ class ContainersController(base.Controller):
neutron_net = neutron_api.get_neutron_network(kwargs.get('network')) neutron_net = neutron_api.get_neutron_network(kwargs.get('network'))
compute_api.network_detach(context, container, neutron_net['id']) compute_api.network_detach(context, container, neutron_net['id'])
pecan.response.status = 202 pecan.response.status = 202
@base.Controller.api_version("1.8")
@pecan.expose('json')
@exception.wrap_pecan_controller_exception
@validation.validate_query_param(pecan.request, schema.network_attach)
def network_attach(self, container_id, **kwargs):
container = _get_container(container_id)
check_policy_on_container(container.as_dict(),
"container:network_attach")
context = pecan.request.context
compute_api = pecan.request.compute_api
neutron_api = neutron.NeutronAPI(context)
neutron_net = neutron_api.get_neutron_network(kwargs.get('network'))
compute_api.network_attach(context, container, neutron_net['id'])

View File

@ -180,3 +180,5 @@ network_detach = {
'required': ['network'], 'required': ['network'],
'additionalProperties': False 'additionalProperties': False
} }
network_attach = copy.deepcopy(network_detach)

View File

@ -40,10 +40,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 1.5 - Add runtime to container * 1.5 - Add runtime to container
* 1.6 - Support detach network from a container * 1.6 - Support detach network from a container
* 1.7 - Disallow non-admin users to force delete containers * 1.7 - Disallow non-admin users to force delete containers
* 1.8 - Support attach a network to a container
""" """
BASE_VER = '1.1' BASE_VER = '1.1'
CURRENT_MAX_VER = '1.7' CURRENT_MAX_VER = '1.8'
class Version(object): class Version(object):

View File

@ -70,3 +70,9 @@ user documentation.
Disallow non-admin users to force delete containers Disallow non-admin users to force delete containers
Only Admin User can use "delete --force" to force delete a container. Only Admin User can use "delete --force" to force delete a container.
1.8
---
Add attach a network to a container.
Users can use this api to attach a neutron network to a container.

View File

@ -134,3 +134,6 @@ class API(object):
def network_detach(self, context, container, *args): def network_detach(self, context, container, *args):
return self.rpcapi.network_detach(context, container, *args) return self.rpcapi.network_detach(context, container, *args)
def network_attach(self, context, container, *args):
return self.rpcapi.network_attach(context, container, *args)

View File

@ -722,3 +722,12 @@ class Manager(periodic_task.PeriodicTasks):
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(reraise=False): with excutils.save_and_reraise_exception(reraise=False):
LOG.exception("Unexpected exception: %s", six.text_type(e)) LOG.exception("Unexpected exception: %s", six.text_type(e))
def network_attach(self, context, container, network):
LOG.debug('Attach network: %(network)s to container: %(container)s.',
{'container': container, 'network': network})
try:
self.driver.network_attach(context, container, network)
except Exception as e:
with excutils.save_and_reraise_exception(reraise=False):
LOG.exception("Unexpected exception: %s", six.text_type(e))

View File

@ -181,3 +181,7 @@ class API(rpc_service.API):
def network_detach(self, context, container, network): def network_detach(self, context, container, network):
return self._call(container.host, 'network_detach', return self._call(container.host, 'network_detach',
container=container, network=network) container=container, network=network)
def network_attach(self, context, container, network):
return self._call(container.host, 'network_attach',
container=container, network=network)

View File

@ -771,6 +771,35 @@ class DockerDriver(driver.ContainerDriver):
container.addresses = update container.addresses = update
container.save(context) container.save(context)
def network_attach(self, context, container, network):
with docker_utils.docker_client() as docker:
network_api = zun_network.api(context,
docker_api=docker)
if network in container.addresses:
raise exception.ZunException('Container %(container)s has'
' alreay connected to the network'
'%(network)s.'
% {'container': container['uuid'],
'network': network})
self._get_or_create_docker_network(context, network_api, network)
requested_network = {'network': network,
'port': '',
'v4-fixed-ip': '',
'v6-fixed-ip': ''}
docker_net_name = self._get_docker_network_name(context, network)
addrs = network_api.connect_container_to_network(
container, docker_net_name, requested_network,
security_groups=None)
if addrs is None:
raise exception.ZunException(_(
'Unexpected missing of addresses'))
update = {}
update[network] = addrs
addresses = container.addresses
addresses.update(update)
container.addresses = addresses
container.save(context)
class NovaDockerDriver(DockerDriver): class NovaDockerDriver(DockerDriver):
capabilities = { capabilities = {

View File

@ -231,3 +231,6 @@ class ContainerDriver(object):
def network_detach(self, context, container, network): def network_detach(self, context, container, network):
raise NotImplementedError() raise NotImplementedError()
def network_attach(self, context, container, network):
raise NotImplementedError()

View File

@ -17,7 +17,7 @@ import webtest
from zun.api import app from zun.api import app
from zun.tests.unit.api import base as api_base from zun.tests.unit.api import base as api_base
CURRENT_VERSION = "container 1.7" CURRENT_VERSION = "container 1.8"
class TestRootController(api_base.FunctionalTest): class TestRootController(api_base.FunctionalTest):
@ -27,7 +27,7 @@ class TestRootController(api_base.FunctionalTest):
'default_version': 'default_version':
{'id': 'v1', {'id': 'v1',
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
'max_version': '1.7', 'max_version': '1.8',
'min_version': '1.1', 'min_version': '1.1',
'status': 'CURRENT'}, 'status': 'CURRENT'},
'description': 'Zun is an OpenStack project which ' 'description': 'Zun is an OpenStack project which '
@ -35,7 +35,7 @@ class TestRootController(api_base.FunctionalTest):
'versions': [{'id': 'v1', 'versions': [{'id': 'v1',
'links': [{'href': 'http://localhost/v1/', 'links': [{'href': 'http://localhost/v1/',
'rel': 'self'}], 'rel': 'self'}],
'max_version': '1.7', 'max_version': '1.8',
'min_version': '1.1', 'min_version': '1.1',
'status': 'CURRENT'}]} 'status': 'CURRENT'}]}

View File

@ -23,7 +23,7 @@ from zun.tests.unit.api import base as api_base
from zun.tests.unit.db import utils from zun.tests.unit.db import utils
from zun.tests.unit.objects import utils as obj_utils from zun.tests.unit.objects import utils as obj_utils
CURRENT_VERSION = "container 1.7" CURRENT_VERSION = "container 1.8"
class TestContainerController(api_base.FunctionalTest): class TestContainerController(api_base.FunctionalTest):

View File

@ -624,3 +624,8 @@ class TestManager(base.TestCase):
container = Container(self.context, **utils.get_test_container()) container = Container(self.context, **utils.get_test_container())
self.compute_manager.network_detach(self.context, container, 'network') self.compute_manager.network_detach(self.context, container, 'network')
mock_detach.assert_called_once_with(self.context, container, mock.ANY) mock_detach.assert_called_once_with(self.context, container, mock.ANY)
@mock.patch.object(fake_driver, 'network_attach')
def test_container_network_attach(self, mock_attach):
container = Container(self.context, **utils.get_test_container())
self.compute_manager.network_attach(self.context, container, 'network')

View File

@ -475,6 +475,26 @@ class TestDockerDriver(base.DriverTestCase):
'network-fake_project', 'network-fake_project',
mock.ANY) mock.ANY)
@mock.patch('zun.network.kuryr_network.KuryrNetwork'
'.connect_container_to_network')
@mock.patch('zun.network.kuryr_network.KuryrNetwork'
'.disconnect_container_from_network')
@mock.patch('zun.network.kuryr_network.KuryrNetwork'
'.list_networks')
def test_network_attach(self, mock_list, mock_disconnect, mock_connect):
mock_container = mock.MagicMock()
mock_container.security_groups = None
mock_list.return_value = {'network': 'network'}
requested_network = [{'network': 'network',
'port': '',
'v4-fixed-ip': '',
'v6-fixed-ip': ''}]
self.driver.network_attach(self.context, mock_container, 'network')
mock_connect.assert_called_once_with(mock_container,
'network-fake_project',
requested_network[0],
security_groups=None)
class TestNovaDockerDriver(base.DriverTestCase): class TestNovaDockerDriver(base.DriverTestCase):
def setUp(self): def setUp(self):