Add 'default_ad_site' field to security service object

Allows to configure optional field 'default_ad_site' from version 2.76.
Restrict to make sure either server or 'default_at_site' provided, but
not both.

APIImpact
Relates-bug: #1988146

Change-Id: I8e21e9170eace134a51efed84de1ccc58eb7eaaa
This commit is contained in:
Kiran Pawar 2023-02-16 09:23:07 +00:00
parent d94d1ae7bd
commit 858939c190
18 changed files with 156 additions and 5 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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",

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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']

View File

@ -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

View File

@ -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):

View File

@ -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'],

View File

@ -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')

View File

@ -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)

View File

@ -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',

View File

@ -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]

View File

@ -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'.