Start building Designate specs
Sadly no schemas, but at least first rough structure. Change-Id: Id2ed7ecfbf20ca522e173cf11f236d00f75871c5
This commit is contained in:
252
codegenerator/openapi/designate.py
Normal file
252
codegenerator/openapi/designate.py
Normal file
@@ -0,0 +1,252 @@
|
||||
# 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.
|
||||
#
|
||||
import inspect
|
||||
from multiprocessing import Process
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
import fixtures
|
||||
|
||||
from codegenerator.common.schema import SpecSchema
|
||||
from codegenerator.openapi.base import OpenStackServerSourceBase
|
||||
from codegenerator.openapi.utils import merge_api_ref_doc
|
||||
|
||||
from ruamel.yaml.scalarstring import LiteralScalarString
|
||||
|
||||
|
||||
class DesignateGenerator(OpenStackServerSourceBase):
|
||||
URL_TAG_MAP = {
|
||||
"/zones/tasks/transfer_accepts": "zone-ownership-transfers-accepts",
|
||||
"/zones/tasks/transfer_requests": "zone-ownership-transfers-requests",
|
||||
"/zones/tasks/imports": "zone-imports",
|
||||
"/zones/tasks/exports": "zone-exports",
|
||||
"/zones/{zone_id}/tasks/export": "zone-exports",
|
||||
"/zones/{zone_id}/tasks": "zone-tasks",
|
||||
"/zones/{zone_id}/recordsets": "recordsets",
|
||||
"/zones/{zone_id}/shares": "shared-zones",
|
||||
"/service_statuses": "service-statuses",
|
||||
"/tlds": "tld",
|
||||
"/tsigkeys": "tsigkey",
|
||||
"/reverse/floatingips": "floatingips",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.api_version = "2.1"
|
||||
self.min_api_version = "2.0"
|
||||
|
||||
def _api_ver_major(self, ver):
|
||||
return ver.ver_major
|
||||
|
||||
def _api_ver_minor(self, ver):
|
||||
return ver.ver_minor
|
||||
|
||||
def _api_ver(self, ver):
|
||||
return (ver.ver_major, ver.ver_minor)
|
||||
|
||||
def _build_routes(
|
||||
self,
|
||||
mapper,
|
||||
node,
|
||||
path="",
|
||||
sub_map: dict[str, str] = {},
|
||||
subcontroller_name: str | None = None,
|
||||
):
|
||||
# path = f"{path}/{subcontroller_name}"
|
||||
for part in [x for x in dir(node) if callable(getattr(node, x))]:
|
||||
# Iterate over functions to find what is exposed on the current
|
||||
# level
|
||||
if part == "_route":
|
||||
continue
|
||||
obj = getattr(node, part)
|
||||
_pecan = getattr(obj, "_pecan", None)
|
||||
exposed = getattr(obj, "exposed", None)
|
||||
if _pecan and exposed:
|
||||
argspec = _pecan.get("argspec", None)
|
||||
args: list[str] = []
|
||||
if argspec:
|
||||
args = argspec.args
|
||||
if "self" in args:
|
||||
args.remove("self")
|
||||
# Only whatever is pecan exposed is of interest
|
||||
conditions = {}
|
||||
action = None
|
||||
url = path
|
||||
resource = None
|
||||
# Check whether for mandatory params there is path defined
|
||||
# If there is entry with all parameters we are most likely on
|
||||
# the subcontroller level, however on the subcontroller there
|
||||
# might be parent_id and id for which we do not have
|
||||
# combination yet, thus take parent_id as base url
|
||||
# for "zone_id" => /zones/{zone_id}/
|
||||
# for "zone_id zone_transfer_request_id" => /zones/{zone_id}
|
||||
# while in transfer_request controller
|
||||
if " ".join(args) in sub_map:
|
||||
url = sub_map[" ".join(args)]
|
||||
if subcontroller_name and subcontroller_name not in url:
|
||||
url += f"/{subcontroller_name}"
|
||||
elif " ".join(args[0:-1]) in sub_map:
|
||||
url = sub_map[" ".join(args[0:-1])]
|
||||
if subcontroller_name and subcontroller_name not in url:
|
||||
url += f"/{subcontroller_name}"
|
||||
url += f"/{{{args[-1]}}}"
|
||||
elif len(args) > 0:
|
||||
url += f"/{{{args[-1]}}}"
|
||||
|
||||
# Identify the action from function name
|
||||
# https://pecan.readthedocs.io/en/latest/rest.html#url-mapping
|
||||
if part == "get_one":
|
||||
conditions["method"] = ["GET"]
|
||||
action = "show"
|
||||
if " ".join(args) not in sub_map:
|
||||
sub_map[" ".join(args)] = url
|
||||
elif part == "get_all":
|
||||
conditions["method"] = ["GET"]
|
||||
action = "list"
|
||||
elif part in ["post", "post_all"]:
|
||||
conditions["method"] = ["POST"]
|
||||
action = "create"
|
||||
elif part in ["put"]:
|
||||
conditions["method"] = ["PUT"]
|
||||
action = "update"
|
||||
elif part in ["patch_one"]:
|
||||
conditions["method"] = ["PATCH"]
|
||||
action = "update"
|
||||
elif part in ["delete", "delete_one"]:
|
||||
conditions["method"] = ["DELETE"]
|
||||
action = "delete"
|
||||
|
||||
if action:
|
||||
# If we identified method as "interesting" register it into
|
||||
# the routes mapper
|
||||
mapper.connect(
|
||||
None,
|
||||
url,
|
||||
controller=obj,
|
||||
action=action,
|
||||
conditions=conditions,
|
||||
)
|
||||
# yield part
|
||||
if not hasattr(node, "__dict__"):
|
||||
return
|
||||
|
||||
for subcontroller, v in (
|
||||
node.__dict__.items()
|
||||
if isinstance(node, type)
|
||||
else node.__class__.__dict__.items()
|
||||
):
|
||||
# Iterate over node attributes for subcontrollers
|
||||
if subcontroller.startswith("_"):
|
||||
continue
|
||||
if subcontroller in ["central_api", "__wrapped__", "SORT_KEYS"]:
|
||||
# Not underested in those
|
||||
continue
|
||||
subpath = f"{path}/{subcontroller}"
|
||||
self._build_routes(mapper, v, subpath, sub_map, subcontroller)
|
||||
|
||||
return
|
||||
|
||||
def generate(self, target_dir, args):
|
||||
proc = Process(target=self._generate, args=[target_dir, args])
|
||||
proc.start()
|
||||
proc.join()
|
||||
if proc.exitcode != 0:
|
||||
raise RuntimeError("Error generating Designate OpenAPI schema")
|
||||
|
||||
def _generate(self, target_dir, args):
|
||||
from designate.api.v2.controllers import root
|
||||
from designate.api.v2.controllers import zones
|
||||
from designate.api.v2.controllers.zones import nameservers
|
||||
from designate.api.v2.controllers.zones import recordsets
|
||||
from designate.api.v2.controllers.zones import sharedzones
|
||||
from designate.api.v2.controllers.zones import tasks
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
# import oslo_messaging as messaging
|
||||
# from oslo_messaging import conffixture as messaging_conffixture
|
||||
from pecan import make_app as pecan_make_app
|
||||
from routes import Mapper
|
||||
|
||||
work_dir = Path(target_dir)
|
||||
work_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
impl_path = Path(
|
||||
work_dir, "openapi_specs", "dns", f"v{self.api_version}.yaml"
|
||||
)
|
||||
impl_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
openapi_spec = self.load_openapi(Path(impl_path))
|
||||
if not openapi_spec:
|
||||
openapi_spec = SpecSchema(
|
||||
info={
|
||||
"title": "OpenStack DNS API",
|
||||
"description": LiteralScalarString(
|
||||
"DNS API provided by Designate service"
|
||||
),
|
||||
"version": self.api_version,
|
||||
},
|
||||
openapi="3.1.0",
|
||||
security=[{"ApiKeyAuth": []}],
|
||||
components={
|
||||
"securitySchemes": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "X-Auth-Token",
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"x-auth-all-projects": {
|
||||
"name": "x-auth-all-projects",
|
||||
"in": "header",
|
||||
"schema": {"type": "boolean"},
|
||||
"description": "If enabled this will show results from all projects in Designate",
|
||||
},
|
||||
"x-auth-sudo-project-id": {
|
||||
"name": "x-auth-sudo-project-id",
|
||||
"in": "header",
|
||||
"schema": {"type": "string", "format": "uuid"},
|
||||
"description": "This allows a user to impersonate another project",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
self._buses = {}
|
||||
|
||||
self.useFixture(
|
||||
fixtures.MonkeyPatch(
|
||||
"designate.central.rpcapi.CentralAPI", mock.MagicMock()
|
||||
)
|
||||
)
|
||||
|
||||
self.app = pecan_make_app(root.RootController())
|
||||
self.root = self.app.application.root
|
||||
|
||||
mapper = Mapper()
|
||||
|
||||
self._build_routes(mapper, root.RootController, "/v2")
|
||||
for route in mapper.matchlist:
|
||||
self._process_route(route, openapi_spec, framework="pecan")
|
||||
|
||||
if args.api_ref_src:
|
||||
merge_api_ref_doc(
|
||||
openapi_spec, args.api_ref_src, allow_strip_version=False
|
||||
)
|
||||
|
||||
self.dump_openapi(openapi_spec, Path(impl_path), args.validate)
|
||||
|
||||
lnk = Path(impl_path.parent, "v2.yaml")
|
||||
lnk.unlink(missing_ok=True)
|
||||
lnk.symlink_to(impl_path.name)
|
||||
|
||||
return impl_path
|
@@ -68,6 +68,11 @@ class OpenApiSchemaGenerator(BaseGenerator):
|
||||
|
||||
ManilaGenerator().generate(target_dir, args)
|
||||
|
||||
def generate_designate(self, target_dir, args):
|
||||
from codegenerator.openapi.designate import DesignateGenerator
|
||||
|
||||
DesignateGenerator().generate(target_dir, args)
|
||||
|
||||
def generate(
|
||||
self, res, target_dir, openapi_spec=None, operation_id=None, args=None
|
||||
):
|
||||
@@ -80,6 +85,8 @@ class OpenApiSchemaGenerator(BaseGenerator):
|
||||
self.generate_nova(target_dir, args)
|
||||
elif args.service_type in ["block-storage", "volume"]:
|
||||
self.generate_cinder(target_dir, args)
|
||||
elif args.service_type == "dns":
|
||||
self.generate_designate(target_dir, args)
|
||||
elif args.service_type == "image":
|
||||
self.generate_glance(target_dir, args)
|
||||
elif args.service_type == "identity":
|
||||
|
@@ -34,3 +34,7 @@ fi
|
||||
if [ -z "$1" -o "$1" = "shared-file-system" ]; then
|
||||
openstack-codegenerator --work-dir wrk --target openapi-spec --service-type shared-file-system --api-ref-src ${API_REF_BUILD_ROOT}/manila/api-ref/build/html/index.html --validate
|
||||
fi
|
||||
|
||||
if [ -z "$1" -o "$1" = "dns" ]; then
|
||||
openstack-codegenerator --work-dir wrk --target openapi-spec --service-type dns --api-ref-src ${API_REF_BUILD_ROOT}/designate/api-ref/build/html/dns-api-v2-index.html --validate
|
||||
fi
|
||||
|
@@ -91,6 +91,35 @@
|
||||
project: "opendev.org/openstack/nova"
|
||||
path: "/api-ref/build/html/index.html"
|
||||
|
||||
- job:
|
||||
name: codegenerator-openapi-dns-tips
|
||||
parent: codegenerator-openapi-tips-base
|
||||
description: |
|
||||
Generate OpenAPI spec for Designate
|
||||
required-projects:
|
||||
- name: openstack/designate
|
||||
|
||||
vars:
|
||||
openapi_service: dns
|
||||
install_additional_projects:
|
||||
- project: "opendev.org/openstack/designate"
|
||||
name: "."
|
||||
|
||||
- job:
|
||||
name: codegenerator-openapi-dns-tips-with-api-ref
|
||||
parent: codegenerator-openapi-dns-tips
|
||||
description: |
|
||||
Generate OpenAPI spec for Designate consuming API-REF
|
||||
required-projects:
|
||||
- name: openstack/designate
|
||||
|
||||
pre-run:
|
||||
- playbooks/openapi/pre-api-ref.yaml
|
||||
vars:
|
||||
codegenerator_api_ref:
|
||||
project: "opendev.org/openstack/designate"
|
||||
path: "/api-ref/build/html/dns-api-v2-index.html"
|
||||
|
||||
- job:
|
||||
name: codegenerator-openapi-identity-tips
|
||||
parent: codegenerator-openapi-tips-base
|
||||
@@ -290,6 +319,8 @@
|
||||
soft: true
|
||||
- name: codegenerator-openapi-compute-tips-with-api-ref
|
||||
soft: true
|
||||
- name: codegenerator-openapi-dns-tips-with-api-ref
|
||||
soft: true
|
||||
- name: codegenerator-openapi-identity-tips-with-api-ref
|
||||
soft: true
|
||||
- name: codegenerator-openapi-image-tips-with-api-ref
|
||||
|
@@ -8,6 +8,7 @@
|
||||
- openstack-tox-py311
|
||||
- codegenerator-openapi-block-storage-tips-with-api-ref
|
||||
- codegenerator-openapi-compute-tips-with-api-ref
|
||||
- codegenerator-openapi-dns-tips-with-api-ref
|
||||
- codegenerator-openapi-identity-tips-with-api-ref
|
||||
- codegenerator-openapi-image-tips-with-api-ref
|
||||
- codegenerator-openapi-load-balancing-tips-with-api-ref
|
||||
@@ -23,6 +24,7 @@
|
||||
- openstack-tox-py311
|
||||
- codegenerator-openapi-block-storage-tips-with-api-ref
|
||||
- codegenerator-openapi-compute-tips-with-api-ref
|
||||
- codegenerator-openapi-dns-tips-with-api-ref
|
||||
- codegenerator-openapi-identity-tips-with-api-ref
|
||||
- codegenerator-openapi-image-tips-with-api-ref
|
||||
- codegenerator-openapi-load-balancing-tips-with-api-ref
|
||||
|
Reference in New Issue
Block a user