Merge "Migrate passthrough to openstacksdk"
This commit is contained in:
commit
eaa6b7c3ba
@ -13,4 +13,4 @@
|
||||
# limitations under the License.
|
||||
"""REST API for Horizon dashboard Javascript code.
|
||||
"""
|
||||
from . import passthrough # noqa
|
||||
from . import designate # noqa
|
||||
|
272
designatedashboard/api/rest/designate.py
Normal file
272
designatedashboard/api/rest/designate.py
Normal file
@ -0,0 +1,272 @@
|
||||
# Copyright (c) 2023 Binero
|
||||
#
|
||||
# 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.
|
||||
|
||||
from designatedashboard.sdk_connection import get_sdk_connection
|
||||
from django.views import generic
|
||||
import logging
|
||||
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
from openstack.dns.v2 import floating_ip as _fip
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _sdk_object_to_list(object):
|
||||
"""Converts an SDK generator object to a list of dictionaries.
|
||||
|
||||
:param object: SDK generator object
|
||||
:returns: List of dictionaries
|
||||
"""
|
||||
result_list = []
|
||||
for item in object:
|
||||
result_list.append(item.to_dict())
|
||||
return result_list
|
||||
|
||||
|
||||
def create_zone(request):
|
||||
"""Create zone."""
|
||||
data = request.DATA
|
||||
|
||||
conn = get_sdk_connection(request)
|
||||
build_kwargs = dict(
|
||||
name=data['name'],
|
||||
email=data['email'],
|
||||
type=data['type'],
|
||||
)
|
||||
if data.get('description', None):
|
||||
build_kwargs['description'] = data['description']
|
||||
if data.get('ttl', None):
|
||||
build_kwargs['ttl'] = data['ttl']
|
||||
if data.get('masters', None):
|
||||
build_kwargs['masters'] = data['masters']
|
||||
|
||||
zone = conn.dns.create_zone(**build_kwargs)
|
||||
return zone.to_dict()
|
||||
|
||||
|
||||
def update_zone(request, **kwargs):
|
||||
"""Update zone."""
|
||||
data = request.DATA
|
||||
zone_id = kwargs.get('zone_id')
|
||||
|
||||
conn = get_sdk_connection(request)
|
||||
build_kwargs = dict(
|
||||
email=data['email'],
|
||||
description=data['description'],
|
||||
ttl=data['ttl'],
|
||||
)
|
||||
zone = conn.dns.update_zone(
|
||||
zone_id, **build_kwargs)
|
||||
return zone.to_dict()
|
||||
|
||||
|
||||
@urls.register
|
||||
class Zones(generic.View):
|
||||
"""API for zones."""
|
||||
|
||||
url_regex = r'dns/v2/zones/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""List zones for current project."""
|
||||
conn = get_sdk_connection(request)
|
||||
zones = _sdk_object_to_list(conn.dns.zones())
|
||||
return {'zones': zones}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create zone."""
|
||||
return create_zone(request)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Zone(generic.View):
|
||||
"""API for zone."""
|
||||
url_regex = r'dns/v2/zones/(?P<zone_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, zone_id):
|
||||
"""Get zone."""
|
||||
conn = get_sdk_connection(request)
|
||||
zone = conn.dns.find_zone(zone_id)
|
||||
return zone.to_dict()
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def patch(self, request, zone_id):
|
||||
"""Edit zone."""
|
||||
kwargs = {'zone_id': zone_id}
|
||||
update_zone(request, **kwargs)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, zone_id):
|
||||
"""Delete zone."""
|
||||
conn = get_sdk_connection(request)
|
||||
conn.dns.delete_zone(zone_id, ignore_missing=True)
|
||||
|
||||
|
||||
def create_recordset(request, **kwargs):
|
||||
"""Create recordset."""
|
||||
data = request.DATA
|
||||
zone_id = kwargs.get('zone_id')
|
||||
|
||||
conn = get_sdk_connection(request)
|
||||
build_kwargs = dict(
|
||||
name=data['name'],
|
||||
type=data['type'],
|
||||
ttl=data['ttl'],
|
||||
records=data['records'],
|
||||
)
|
||||
if data.get('description', None):
|
||||
build_kwargs['description'] = data['description']
|
||||
|
||||
rs = conn.dns.create_recordset(
|
||||
zone_id, **build_kwargs)
|
||||
return rs.to_dict()
|
||||
|
||||
|
||||
def update_recordset(request, **kwargs):
|
||||
"""Update recordset."""
|
||||
data = request.DATA
|
||||
zone_id = kwargs.get('zone_id')
|
||||
rs_id = kwargs.get('rs_id')
|
||||
|
||||
conn = get_sdk_connection(request)
|
||||
|
||||
build_kwargs = dict()
|
||||
if data.get('description', None):
|
||||
build_kwargs['description'] = data['description']
|
||||
if data.get('ttl', None):
|
||||
build_kwargs['ttl'] = data['ttl']
|
||||
if data.get('records', None):
|
||||
build_kwargs['records'] = data['records']
|
||||
|
||||
build_kwargs['zone_id'] = zone_id
|
||||
rs = conn.dns.update_recordset(
|
||||
rs_id, **build_kwargs)
|
||||
return rs.to_dict()
|
||||
|
||||
|
||||
def _populate_zone_id(items, zone_id):
|
||||
for item in items:
|
||||
item['zone_id'] = zone_id
|
||||
return items
|
||||
|
||||
|
||||
@urls.register
|
||||
class RecordSets(generic.View):
|
||||
"""API for recordsets."""
|
||||
url_regex = r'dns/v2/zones/(?P<zone_id>[^/]+)/recordsets/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, zone_id):
|
||||
"""Get recordsets."""
|
||||
conn = get_sdk_connection(request)
|
||||
rsets = _sdk_object_to_list(conn.dns.recordsets(zone_id))
|
||||
return {'recordsets': _populate_zone_id(rsets, zone_id)}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request, zone_id):
|
||||
"""Create recordset."""
|
||||
kwargs = {'zone_id': zone_id}
|
||||
return create_recordset(request, **kwargs)
|
||||
|
||||
|
||||
@urls.register
|
||||
class RecordSet(generic.View):
|
||||
"""API for recordset."""
|
||||
url_regex = r'dns/v2/zones/(?P<zone_id>[^/]+)/recordsets/(?P<rs_id>[^/]+)/$' # noqa
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, zone_id, rs_id):
|
||||
"""Get recordset."""
|
||||
conn = get_sdk_connection(request)
|
||||
rs = conn.dns.get_recordset(rs_id, zone_id)
|
||||
rs_dict = rs.to_dict()
|
||||
rs_dict['zone_id'] = zone_id
|
||||
return rs_dict
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, zone_id, rs_id):
|
||||
"""Edit recordset."""
|
||||
kwargs = {'zone_id': zone_id, 'rs_id': rs_id}
|
||||
update_recordset(request, **kwargs)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, zone_id, rs_id):
|
||||
"""Delete recordset."""
|
||||
conn = get_sdk_connection(request)
|
||||
conn.dns.delete_recordset(rs_id, zone_id, ignore_missing=True)
|
||||
|
||||
|
||||
@urls.register
|
||||
class DnsFloatingIps(generic.View):
|
||||
"""API for floatingips."""
|
||||
url_regex = r'dns/v2/reverse/floatingips/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get floatingips."""
|
||||
conn = get_sdk_connection(request)
|
||||
fips = _sdk_object_to_list(conn.dns.floating_ips())
|
||||
return {'floatingips': fips}
|
||||
|
||||
|
||||
def update_dns_floatingip(request, **kwargs):
|
||||
"""Update recordset."""
|
||||
data = request.DATA
|
||||
fip_id = kwargs.get('fip_id')
|
||||
|
||||
conn = get_sdk_connection(request)
|
||||
|
||||
build_kwargs = dict(
|
||||
ptrdname=data['ptrdname'],
|
||||
)
|
||||
if data.get('description', None):
|
||||
build_kwargs['description'] = data['description']
|
||||
if data.get('ttl', None):
|
||||
build_kwargs['ttl'] = data['ttl']
|
||||
|
||||
# TODO(tobias-urdin): Bug in openstacksdk
|
||||
# https://review.opendev.org/c/openstack/openstacksdk/+/903879
|
||||
obj = conn.dns._get_resource(
|
||||
_fip.FloatingIP, fip_id, **build_kwargs)
|
||||
obj.resource_key = None
|
||||
has_body = True
|
||||
if build_kwargs['ptrdname'] is None:
|
||||
has_body = False
|
||||
fip = obj.commit(conn.dns, has_body=has_body)
|
||||
|
||||
return fip.to_dict()
|
||||
|
||||
|
||||
@urls.register
|
||||
class DnsFloatingIp(generic.View):
|
||||
"""API for dns floatingip."""
|
||||
url_regex = r'dns/v2/reverse/floatingips/(?P<fip_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, fip_id):
|
||||
"""Get floatingip."""
|
||||
conn = get_sdk_connection(request)
|
||||
fip = conn.dns.get_floating_ip(fip_id)
|
||||
return fip.to_dict()
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def patch(self, request, fip_id):
|
||||
"""Edit floatingip."""
|
||||
kwargs = {'fip_id': fip_id}
|
||||
update_dns_floatingip(request, **kwargs)
|
@ -1,119 +0,0 @@
|
||||
# Copyright 2016, Hewlett Packard Enterprise Development, LP
|
||||
#
|
||||
# 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.
|
||||
"""API for the passthrough service.
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.views import generic
|
||||
import functools
|
||||
import logging
|
||||
import requests
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from horizon import exceptions
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _passthrough_request(request_method, url,
|
||||
request, data=None, params=None):
|
||||
"""Makes a request to the appropriate service API with an optional payload.
|
||||
|
||||
Should set any necessary auth headers and SSL parameters.
|
||||
"""
|
||||
|
||||
# Set verify if a CACERT is set and SSL_NO_VERIFY isn't True
|
||||
verify = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
if getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False):
|
||||
verify = False
|
||||
|
||||
service_url = _get_service_url(request, 'dns')
|
||||
request_url = '{}{}'.format(
|
||||
service_url,
|
||||
url if service_url.endswith('/') else ('/' + url)
|
||||
)
|
||||
|
||||
response = request_method(
|
||||
request_url,
|
||||
headers={'X-Auth-Token': request.user.token.id},
|
||||
json=data,
|
||||
verify=verify,
|
||||
params=params
|
||||
)
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except HTTPError as e:
|
||||
LOG.debug(e.response.content)
|
||||
for error in rest_utils.http_errors:
|
||||
if (e.response.status_code == getattr(error, 'status_code', 0) and
|
||||
exceptions.HorizonException in error.__bases__):
|
||||
raise error
|
||||
raise
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# Create some convenience partial functions
|
||||
passthrough_get = functools.partial(_passthrough_request, requests.get)
|
||||
passthrough_post = functools.partial(_passthrough_request, requests.post)
|
||||
passthrough_put = functools.partial(_passthrough_request, requests.put)
|
||||
passthrough_patch = functools.partial(_passthrough_request, requests.patch)
|
||||
passthrough_delete = functools.partial(_passthrough_request, requests.delete)
|
||||
|
||||
|
||||
def _get_service_url(request, service):
|
||||
"""Get service's URL from keystone; allow an override in settings"""
|
||||
service_url = getattr(settings, service.upper() + '_URL', None)
|
||||
try:
|
||||
service_url = base.url_for(request, service)
|
||||
except exceptions.ServiceCatalogException:
|
||||
pass
|
||||
# Currently the keystone endpoint is http://host:port/
|
||||
# without the version.
|
||||
return service_url
|
||||
|
||||
|
||||
@urls.register
|
||||
class Passthrough(generic.View):
|
||||
"""Pass-through API for executing service requests.
|
||||
|
||||
Horizon only adds auth and CORS proxying.
|
||||
"""
|
||||
url_regex = r'dns/(?P<path>.+)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, path):
|
||||
return passthrough_get(path, request).json()
|
||||
|
||||
@rest_utils.ajax()
|
||||
def post(self, request, path):
|
||||
data = dict(request.DATA) if request.DATA else {}
|
||||
return passthrough_post(path, request, data).json()
|
||||
|
||||
@rest_utils.ajax()
|
||||
def put(self, request, path):
|
||||
data = dict(request.DATA) if request.DATA else {}
|
||||
return passthrough_put(path, request, data).json()
|
||||
|
||||
@rest_utils.ajax()
|
||||
def patch(self, request, path):
|
||||
data = dict(request.DATA) if request.DATA else {}
|
||||
return passthrough_patch(path, request, data).json()
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, path):
|
||||
return passthrough_delete(path, request).json()
|
50
designatedashboard/sdk_connection.py
Normal file
50
designatedashboard/sdk_connection.py
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright Red Hat
|
||||
#
|
||||
# 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.
|
||||
from django.conf import settings
|
||||
|
||||
import designatedashboard
|
||||
from openstack import config as occ
|
||||
from openstack import connection
|
||||
|
||||
|
||||
def get_sdk_connection(request):
|
||||
"""Creates an SDK connection based on the request.
|
||||
|
||||
:param request: Django request object
|
||||
:returns: SDK connection object
|
||||
"""
|
||||
# NOTE(mordred) Nothing says love like two inverted booleans
|
||||
# The config setting is NO_VERIFY which is, in fact, insecure.
|
||||
# get_one_cloud wants verify, so we pass 'not insecure' to verify.
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
# Pass interface to honor 'OPENSTACK_ENDPOINT_TYPE'
|
||||
interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')
|
||||
# Pass load_yaml_config as this is a Django service with its own config
|
||||
# and we don't want to accidentally pick up a clouds.yaml file. We want to
|
||||
# use the settings we're passing in.
|
||||
cloud_config = occ.OpenStackConfig(load_yaml_config=False).get_one_cloud(
|
||||
verify=not insecure,
|
||||
cacert=cacert,
|
||||
interface=interface,
|
||||
region_name=request.user.services_region,
|
||||
auth_type='token',
|
||||
auth=dict(
|
||||
project_id=request.user.project_id,
|
||||
project_domain_id=request.user.domain_id,
|
||||
auth_token=request.user.token.unscoped_token,
|
||||
auth_url=request.user.endpoint),
|
||||
app_name='designate-dashboard',
|
||||
app_version=designatedashboard.__version__)
|
||||
return connection.from_config(cloud_config=cloud_config)
|
@ -61,7 +61,7 @@
|
||||
*/
|
||||
function list(params) {
|
||||
var config = params ? {params: params} : {};
|
||||
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips', config)
|
||||
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/', config)
|
||||
.catch(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve the floating ip PTRs.'));
|
||||
});
|
||||
@ -69,7 +69,7 @@
|
||||
|
||||
function get(id, params) {
|
||||
var config = params ? {params: params} : {};
|
||||
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/' + id, config)
|
||||
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/' + id + '/', config)
|
||||
.catch(function () {
|
||||
toastService.add('error', gettext('Unable to get the floating ip PTR ' + id));
|
||||
});
|
||||
@ -95,7 +95,7 @@
|
||||
ttl: data.ttl
|
||||
};
|
||||
return httpService.patch(
|
||||
apiPassthroughUrl + 'v2/reverse/floatingips/' + floatingIpID, apiData)
|
||||
apiPassthroughUrl + 'v2/reverse/floatingips/' + floatingIpID + '/', apiData)
|
||||
.catch(function () {
|
||||
toastService.add('error', gettext('Unable to set the floating IP PTR record.'));
|
||||
});
|
||||
|
@ -130,7 +130,7 @@
|
||||
records: data.records
|
||||
};
|
||||
return httpService.put(
|
||||
apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId, apiData)
|
||||
apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/', apiData)
|
||||
.catch(function () {
|
||||
toastService.add('error', gettext('Unable to update the record set.'));
|
||||
});
|
||||
|
4
releasenotes/notes/openstacksdk-11483491f9978bd1.yaml
Normal file
4
releasenotes/notes/openstacksdk-11483491f9978bd1.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The designate dashboard now needs openstacksdk
|
@ -8,3 +8,4 @@
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
|
||||
horizon>=17.1.0 # Apache-2.0
|
||||
openstacksdk>=0.62.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user