diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 27a29bc497..1f1ae3162d 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -2418,6 +2418,20 @@ scheduler_hints: required: false type: object min_version: 2.65 +security_service_default_ad_site: + description: | + The security service default AD site. + in: body + required: true + type: string + min_version: 2.76 +security_service_default_ad_site_request: + description: | + The security service default AD site. + in: body + required: false + type: string + min_version: 2.76 security_service_dns_ip: description: | The DNS IP address that is used inside the project network. diff --git a/api-ref/source/samples/security-service-create-response.json b/api-ref/source/samples/security-service-create-response.json index 61f892bdab..25f4ba6cc0 100644 --- a/api-ref/source/samples/security-service-create-response.json +++ b/api-ref/source/samples/security-service-create-response.json @@ -8,6 +8,7 @@ "created_at": "2015-09-07T12:19:10.695211", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", diff --git a/api-ref/source/samples/security-service-show-response.json b/api-ref/source/samples/security-service-show-response.json index c85849cd9f..53c6834fb8 100644 --- a/api-ref/source/samples/security-service-show-response.json +++ b/api-ref/source/samples/security-service-show-response.json @@ -8,6 +8,7 @@ "created_at": "2015-09-07T12:19:10.000000", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", diff --git a/api-ref/source/samples/security-services-list-detailed-response.json b/api-ref/source/samples/security-services-list-detailed-response.json index 956092b476..03c41a0195 100644 --- a/api-ref/source/samples/security-services-list-detailed-response.json +++ b/api-ref/source/samples/security-services-list-detailed-response.json @@ -10,6 +10,7 @@ "description": "Creating my first Security Service", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", @@ -27,6 +28,7 @@ "description": "Creating my second Security Service", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": null, "password": null, diff --git a/api-ref/source/samples/security-services-list-for-share-network-response.json b/api-ref/source/samples/security-services-list-for-share-network-response.json index 5f42fe15c1..edfab5b8be 100644 --- a/api-ref/source/samples/security-services-list-for-share-network-response.json +++ b/api-ref/source/samples/security-services-list-for-share-network-response.json @@ -10,6 +10,7 @@ "description": "Creating my first Security Service", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": "demo", "password": "supersecret", @@ -29,6 +30,7 @@ "description": "Creating my second Security Service", "updated_at": null, "server": null, + "default_ad_site": null, "dns_ip": "10.0.0.0/24", "user": null, "password": null, diff --git a/api-ref/source/security-services.inc b/api-ref/source/security-services.inc index 60af10ae8c..4f1e2dc8a8 100644 --- a/api-ref/source/security-services.inc +++ b/api-ref/source/security-services.inc @@ -32,6 +32,8 @@ You can configure a security service with these options: - The password for the user, if you specify a user name. +- A default AD site, optional (available starting with API version 2.76) + A security service resource can also be given a user defined name and description. @@ -125,6 +127,7 @@ Response parameters - domain: security_service_domain - ou: security_service_ou - server: security_service_server + - default_ad_site: security_service_default_ad_site - updated_at: updated_at - created_at: created_at @@ -181,6 +184,7 @@ Response parameters - domain: security_service_domain - ou: security_service_ou - server: security_service_server + - default_ad_site: security_service_default_ad_site - updated_at: updated_at - created_at: created_at @@ -227,6 +231,7 @@ Request - domain: security_service_domain_request - ou: security_service_ou_request - server: security_service_server_request + - default_ad_site: security_service_default_ad_site_request Request example --------------- @@ -251,6 +256,7 @@ Response parameters - domain: security_service_domain - ou: security_service_ou - server: security_service_server + - default_ad_site: security_service_default_ad_site - updated_at: updated_at - created_at: created_at @@ -304,6 +310,7 @@ Request - domain: security_service_domain_request - ou: security_service_ou_request - server: security_service_server_request + - default_ad_site: security_service_default_ad_site_request Request example --------------- @@ -328,6 +335,7 @@ Response parameters - domain: security_service_domain - ou: security_service_ou - server: security_service_server + - default_ad_site: security_service_default_ad_site - updated_at: updated_at - created_at: created_at diff --git a/manila/api/openstack/api_version_request.py b/manila/api/openstack/api_version_request.py index 0ee7d9a092..cab7039825 100644 --- a/manila/api/openstack/api_version_request.py +++ b/manila/api/openstack/api_version_request.py @@ -192,6 +192,7 @@ REST_API_VERSION_HISTORY = """ 'error' state. * 2.75 - Added option to specify quiesce wait time in share replica promote API. + * 2.76 - Added 'default_ad_site' field in security service object. """ @@ -199,7 +200,7 @@ REST_API_VERSION_HISTORY = """ # The default api version request is defined to be the # minimum version of the API supported. _MIN_API_VERSION = "2.0" -_MAX_API_VERSION = "2.75" +_MAX_API_VERSION = "2.76" DEFAULT_API_VERSION = _MIN_API_VERSION diff --git a/manila/api/openstack/rest_api_version_history.rst b/manila/api/openstack/rest_api_version_history.rst index af38ea4005..dae4aaa272 100644 --- a/manila/api/openstack/rest_api_version_history.rst +++ b/manila/api/openstack/rest_api_version_history.rst @@ -410,3 +410,11 @@ ____ 2.74 ---- Allow/deny share access rule even if share replicas are in 'error' state. + +2.75 +---- + Added option to specify quiesce wait time in share replica promote API. + +2.76 +---- + Added 'default_ad_site' field in security service object. diff --git a/manila/api/v1/security_service.py b/manila/api/v1/security_service.py index 6fe5805278..2d6b47075a 100644 --- a/manila/api/v1/security_service.py +++ b/manila/api/v1/security_service.py @@ -22,6 +22,7 @@ import webob from webob import exc from manila.api import common +from manila.api.openstack import api_version_request as api_version from manila.api.openstack import wsgi from manila.api.views import security_service as security_service_views from manila.common import constants @@ -192,6 +193,21 @@ class SecurityServiceController(wsgi.Controller): "fields are available for update.") % id raise exc.HTTPForbidden(explanation=msg) + server = security_service_data.get('server') + default_ad_site = security_service_data.get('default_ad_site') + if default_ad_site: + if req.api_version_request < api_version.APIVersionRequest("2.76"): + msg = _('"default_ad_site" is only supported from API ' + 'version 2.76.') + raise webob.exc.HTTPBadRequest(explanation=msg) + + if (security_service['type'] == 'active_directory' and server and + default_ad_site): + raise exception.InvalidInput( + reason=(_("Cannot create security service because both " + "server and 'default_ad_site' were provided. " + "Specify either server or 'default_ad_site'."))) + policy.check_policy(context, RESOURCE_NAME, 'update', security_service) security_service = db.security_service_update( context, id, security_service_data) @@ -214,6 +230,20 @@ class SecurityServiceController(wsgi.Controller): "service. Valid types are %(types)s") % {'type': security_srv_type, 'types': ','.join(allowed_types)})) + server = security_service_args.get('server') + default_ad_site = security_service_args.get('default_ad_site') + if default_ad_site: + if req.api_version_request < api_version.APIVersionRequest("2.76"): + msg = _('"default_ad_site" is only supported from API ' + 'version 2.76.') + raise webob.exc.HTTPBadRequest(explanation=msg) + + if (security_srv_type == 'active_directory' and server and + default_ad_site): + raise exception.InvalidInput( + reason=(_("Cannot create security service because both " + "server and 'default_ad_site' were provided, " + "Specify either server or 'default_ad_site'."))) security_service_args['project_id'] = context.project_id security_service = db.security_service_create( context, security_service_args) diff --git a/manila/api/views/security_service.py b/manila/api/views/security_service.py index 62dd6a976f..b1d6a56d14 100644 --- a/manila/api/views/security_service.py +++ b/manila/api/views/security_service.py @@ -23,6 +23,7 @@ class ViewBuilder(common.ViewBuilder): _collection_name = 'security_services' _detail_version_modifiers = [ 'add_ou_to_security_service', + 'add_default_ad_site_to_security_service', ] def summary_list(self, request, security_services): @@ -64,6 +65,10 @@ class ViewBuilder(common.ViewBuilder): def add_ou_to_security_service(self, context, ss_dict, ss): ss_dict['ou'] = ss.get('ou') + @common.ViewBuilder.versioned_method("2.76") + def add_default_ad_site_to_security_service(self, context, ss_dict, ss): + ss_dict['default_ad_site'] = ss.get('default_ad_site') + def _list_view(self, func, request, security_services): """Provide a view for a list of security services.""" security_services_list = [func(request, service)['security_service'] diff --git a/manila/db/migrations/alembic/versions/c476aeb186ec_add_default_ad_site_to_security_service.py b/manila/db/migrations/alembic/versions/c476aeb186ec_add_default_ad_site_to_security_service.py new file mode 100644 index 0000000000..bb5d6b2d20 --- /dev/null +++ b/manila/db/migrations/alembic/versions/c476aeb186ec_add_default_ad_site_to_security_service.py @@ -0,0 +1,49 @@ +# 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. + +"""add default_ad_site to security service + +Revision ID: c476aeb186ec +Revises: bb5938d74b73 +Create Date: 2022-11-30 10:59:34.866946 + +""" + +# revision identifiers, used by Alembic. +revision = 'c476aeb186ec' +down_revision = 'bb5938d74b73' + +from alembic import op +from oslo_log import log +import sqlalchemy as sa + +LOG = log.getLogger(__name__) + +ss_table_name = 'security_services' + + +def upgrade(): + try: + op.add_column( + ss_table_name, + sa.Column('default_ad_site', sa.String(255), nullable=True)) + except Exception: + LOG.error("%s table column default_ad_site not added", ss_table_name) + raise + + +def downgrade(): + try: + op.drop_column(ss_table_name, 'default_ad_site') + except Exception: + LOG.error("%s table column default_ad_site not dropped", ss_table_name) + raise diff --git a/manila/db/sqlalchemy/models.py b/manila/db/sqlalchemy/models.py index d4acc21f75..e888b66eac 100644 --- a/manila/db/sqlalchemy/models.py +++ b/manila/db/sqlalchemy/models.py @@ -931,6 +931,7 @@ class SecurityService(BASE, ManilaBase): name = Column(String(255), nullable=True) description = Column(String(255), nullable=True) ou = Column(String(255), nullable=True) + default_ad_site = Column(String(255), nullable=True) class ShareNetwork(BASE, ManilaBase): diff --git a/manila/share/manager.py b/manila/share/manager.py index a4e56a02e4..e7f0d0245b 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -3246,6 +3246,7 @@ class ShareManager(manager.SchedulerDependentManager): data = { 'name': security_service['name'], 'ou': security_service['ou'], + 'default_ad_site': security_service['default_ad_site'], 'domain': security_service['domain'], 'server': security_service['server'], 'dns_ip': security_service['dns_ip'], @@ -4205,6 +4206,7 @@ class ShareManager(manager.SchedulerDependentManager): 'user': security_service['user'], 'type': ss_type, 'password': security_service['password'], + 'default_ad_site': security_service['default_ad_site'], } self.db.share_server_backend_details_set( context, share_server['id'], @@ -5843,6 +5845,7 @@ class ShareManager(manager.SchedulerDependentManager): backend_details_data = { 'name': new_security_service['name'], 'ou': new_security_service['ou'], + 'default_ad_site': new_security_service['default_ad_site'], 'domain': new_security_service['domain'], 'server': new_security_service['server'], 'dns_ip': new_security_service['dns_ip'], diff --git a/manila/tests/api/v1/test_security_service.py b/manila/tests/api/v1/test_security_service.py index 35cd1e7f64..7f90c5f4bd 100644 --- a/manila/tests/api/v1/test_security_service.py +++ b/manila/tests/api/v1/test_security_service.py @@ -140,6 +140,14 @@ class ShareApiTest(test.TestCase): self.assertRaises(exception.InvalidInput, self.controller.create, req, {"security_service": sec_service}) + @ddt.data('2.76') + def test_security_service_create_invalid_active_directory(self, version): + sec_service = self.ss_active_directory.copy() + sec_service['default_ad_site'] = 'fake_default_ad_site' + req = fakes.HTTPRequest.blank('/security-services', version=version) + self.assertRaises(exception.InvalidInput, self.controller.create, req, + {"security_service": sec_service}) + def test_create_security_service_no_body(self): body = {} req = fakes.HTTPRequest.blank('/security-services') diff --git a/manila/tests/api/v2/test_security_services.py b/manila/tests/api/v2/test_security_services.py index 060e1c5c6e..85e8b29c19 100644 --- a/manila/tests/api/v2/test_security_services.py +++ b/manila/tests/api/v2/test_security_services.py @@ -43,6 +43,8 @@ def stub_security_service(self, version, id): ) if self.is_microversion_ge(version, '2.44'): ss_dict['ou'] = 'fake-ou' + if self.is_microversion_ge(version, '2.76'): + ss_dict['default_ad_site'] = 'fake-default_ad_site' return ss_dict @@ -53,6 +55,7 @@ class SecurityServicesAPITest(test.TestCase): ('2.0'), ('2.43'), ('2.44'), + ('2.76'), ) def test_index(self, version): ss = [ @@ -85,3 +88,8 @@ class SecurityServicesAPITest(test.TestCase): self.assertIn('ou', ss_keys) else: self.assertNotIn('ou', ss_keys) + + if self.is_microversion_ge(version, '2.76'): + self.assertIn('default_ad_site', ss_keys) + else: + self.assertNotIn('default_ad_site', ss_keys) diff --git a/manila/tests/db/sqlalchemy/test_api.py b/manila/tests/db/sqlalchemy/test_api.py index 3b830bba13..8dd3b0da81 100644 --- a/manila/tests/db/sqlalchemy/test_api.py +++ b/manila/tests/db/sqlalchemy/test_api.py @@ -45,6 +45,7 @@ security_service_dict = { 'dns_ip': 'fake dns', 'server': 'fake ldap server', 'domain': 'fake ldap domain', + 'default_ad_site': 'fake ldap default_ad_site', 'ou': 'fake ldap ou', 'user': 'fake user', 'password': 'fake password', @@ -3232,6 +3233,7 @@ class SecurityServiceDatabaseAPITestCase(BaseDatabaseAPITestCase): 'dns_ip': 'new dns', 'server': 'new ldap server', 'domain': 'new ldap domain', + 'default_ad_site': 'new ldap default_ad_site', 'ou': 'new ldap ou', 'user': 'new user', 'password': 'new password', diff --git a/manila/tests/share/test_manager.py b/manila/tests/share/test_manager.py index 28a6bab65b..4a69d39201 100644 --- a/manila/tests/share/test_manager.py +++ b/manila/tests/share/test_manager.py @@ -3539,6 +3539,7 @@ class ShareManagerTestCase(test.TestCase): 'user': 'fake_user' + ss_type, 'type': ss_type, 'password': 'fake_password' + ss_type, + 'default_ad_site': 'fake_default_ad_site' + ss_type, }) sec_services = network_info['security_services'] server_info = {'fake_server_info_key': 'fake_server_info_value'} @@ -6491,6 +6492,7 @@ class ShareManagerTestCase(test.TestCase): ss_data_from_db = { 'name': ss_from_db['name'], 'ou': ss_from_db['ou'], + 'default_ad_site': ss_from_db['default_ad_site'], 'domain': ss_from_db['domain'], 'server': ss_from_db['server'], 'dns_ip': ss_from_db['dns_ip'], @@ -9515,8 +9517,8 @@ class ShareManagerTestCase(test.TestCase): fake_rules = ['fake_rules'] network_info = {'fake': 'fake'} backend_details_keys = [ - 'name', 'ou', 'domain', 'server', 'dns_ip', 'user', 'type', - 'password'] + 'name', 'ou', 'default_ad_site', 'domain', 'server', 'dns_ip', + 'user', 'type', 'password'] backend_details_data = {} [backend_details_data.update( {key: security_services[0][key]}) for key in backend_details_keys] @@ -9673,8 +9675,8 @@ class ShareManagerTestCase(test.TestCase): new_security_service_id = security_services[1]['id'] network_info = [{'fake': 'fake'}] backend_details_keys = [ - 'name', 'ou', 'domain', 'server', 'dns_ip', 'user', 'type', - 'password'] + 'name', 'ou', 'default_ad_site', 'domain', 'server', 'dns_ip', + 'user', 'type', 'password'] backend_details_data = {} [backend_details_data.update( {key: security_services[0][key]}) for key in backend_details_keys] diff --git a/releasenotes/notes/add-defaultadsite-to-security-service-e90854c1a69be581.yaml b/releasenotes/notes/add-defaultadsite-to-security-service-e90854c1a69be581.yaml new file mode 100644 index 0000000000..035e42f081 --- /dev/null +++ b/releasenotes/notes/add-defaultadsite-to-security-service-e90854c1a69be581.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + From API version 2.76, added 'default_ad_site' field to 'security_service' + object. This field can not be used along-with 'server' field of the + 'security_service'.