murano-dashboard/muranodashboard/environments/topology.py
zhurong 87f1b5b658 Django 2.0 support and fix lower-constraints tox env
Replace django.core.urlresolves with django.urls

(In Django 2.0) The django.core.urlresolvers module is removed
in favor of its new location, django.urls.
It was deprecated in Django 1.10:
https://docs.djangoproject.com/en/2.0/releases/1.10/#id3

Add py35dj20 job to test Django 2.0 integration.
And update the lower-constraints.

Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
Co-Authored-By: Ellen Batbouta <ellen.batbouta@oracle.com>

Change-Id: I3456162c934eb48ccf180d80b5cf7ff5614221c0
2018-06-13 12:03:55 +00:00

312 lines
10 KiB
Python

# Copyright (c) 2014 Mirantis, Inc.
#
# 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 json
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.template import loader
from django.urls import reverse
import six
from muranodashboard.api import packages as pkg_cli
from muranodashboard.environments import consts
def get_app_image(request, app_fqdn, status=None):
if '@' in app_fqdn:
class_fqn, package_fqn = app_fqdn.split('@')
if '/' in class_fqn:
class_fqn, version = class_fqn.split('/')
else:
version = None
else:
package_fqn = app_fqdn
version = None
package = pkg_cli.app_by_fqn(request, package_fqn, version=version)
if status in [
consts.STATUS_ID_DEPLOY_FAILURE,
consts.STATUS_ID_DELETE_FAILURE,
]:
url = static('dashboard/img/stack-red.svg')
elif status == consts.STATUS_ID_READY:
url = static('dashboard/img/stack-green.svg')
else:
url = static('dashboard/img/stack-gray.svg')
if package:
app_id = package.id
url = reverse("horizon:app-catalog:catalog:images", args=(app_id,))
return url
def _get_environment_status_message(entity):
if hasattr(entity, 'status'):
status = entity.status
else:
status = entity['?']['status']
in_progress = True
status_message = ''
if status in (consts.STATUS_ID_PENDING, consts.STATUS_ID_READY):
in_progress = False
if status == consts.STATUS_ID_PENDING:
status_message = 'Waiting for deployment'
elif status == consts.STATUS_ID_READY:
status_message = 'Deployed'
elif status == consts.STATUS_ID_DEPLOYING:
status_message = 'Deployment is in progress'
elif status == consts.STATUS_ID_DEPLOY_FAILURE:
status_message = 'Deployment failed'
return in_progress, status_message
def _truncate_type(type_str, num_of_chars):
if len(type_str) < num_of_chars:
return type_str
else:
parts = type_str.split('.')
type_str, type_len = parts[-1], len(parts[-1])
for part in reversed(parts[:-1]):
if type_len + len(part) + 1 > num_of_chars:
return '...' + type_str
else:
type_str = part + '.' + type_str
type_len += len(part) + 1
return type_str
def _application_info(application, app_image, status):
name = application['?'].get('name')
if not name:
name = application.get('name')
context = {'name': name,
'type': _truncate_type(application['?']['type'], 45),
'status': status,
'app_image': app_image}
return loader.render_to_string('services/_application_info.html',
context)
def _network_info(name, image):
context = {'name': name,
'image': image}
return loader.render_to_string('services/_network_info.html',
context)
def _unit_info(unit, unit_image):
data = dict(unit)
data['type'] = _truncate_type(data['type'], 45)
context = {'data': data,
'unit_image': unit_image}
return loader.render_to_string('services/_unit_info.html', context)
def _environment_info(environment, status):
context = {'name': environment.name,
'status': status}
return loader.render_to_string('services/_environment_info.html',
context)
def _create_empty_node():
node = {
'name': '',
'status': 'ready',
'image': '',
'image_size': 60,
'required_by': [],
'image_x': -30,
'image_y': -30,
'text_x': 40,
'text_y': ".35em",
'link_type': "relation",
'in_progress': False,
'info_box': ''
}
return node
def _create_ext_network_node(name):
node = _create_empty_node()
node.update({'id': name,
'image': static('muranodashboard/images/ext-net.png'),
'link_type': 'relation',
'info_box': _network_info(name, static(
'dashboard/img/lb-green.svg'))}
)
return node
def _convert_lists(node_data):
for key, value in six.iteritems(node_data):
if isinstance(value, list) and all(
map(lambda s: not isinstance(s, (dict, list)), value)):
new_value = ', '.join(str(v) for v in value)
node_data[key] = new_value
def _split_seq_by_predicate(seq, predicate):
holds, not_holds = [], []
for elt in seq:
if predicate(elt):
holds.append(elt)
else:
not_holds.append(elt)
return holds, not_holds
def _is_atomic(elt):
key, value = elt
return not isinstance(value, (dict, list))
def render_d3_data(request, environment):
if not (environment and environment.services):
return None
ext_net_name = None
d3_data = {"nodes": [], "environment": {}}
in_progress, status_message = _get_environment_status_message(environment)
environment_node = _create_empty_node()
environment_node.update({
'id': environment.id,
'name': environment.name,
'status': status_message,
'image': static('dashboard/img/stack-green.svg'),
'in_progress': in_progress,
'info_box': _environment_info(environment, status_message)
})
d3_data['environment'] = environment_node
unit_image_active = static('dashboard/img/server-green.svg')
unit_image_non_active = static('dashboard/img/server-gray.svg')
node_refs = {}
def get_image(fqdn, node_data):
if fqdn.startswith('io.murano.resources'):
if len(node_data.get('ipAddresses', [])) > 0:
image = unit_image_active
else:
image = unit_image_non_active
else:
image = get_app_image(request, fqdn)
return image
def rec(node_data, node_key, parent_node=None):
if not isinstance(node_data, dict):
return
_convert_lists(node_data)
node_type = node_data.get('?', {}).get('type')
node_id = node_data.get('?', {}).get('id')
atomics, containers = _split_seq_by_predicate(
six.iteritems(node_data), _is_atomic)
if node_type and node_data is not parent_node:
node = _create_empty_node()
node_refs[node_id] = node
atomics.extend([('id', node_data['?']['id']),
('type', node_type),
('name', node_data.get('name', node_key))])
image = get_image(node_type, node_data)
node.update({
'id': node_id,
'info_box': _unit_info(atomics, image),
'image': image,
'link_type': 'unit',
'in_progress': in_progress})
d3_data['nodes'].append(node)
for key, value in containers:
if key == '?':
continue
if isinstance(value, dict):
rec(value, key, node_data)
elif isinstance(value, list):
for index, val in enumerate(value):
rec(val, '{0}[{1}]'.format(key, index), node_data)
def build_links_rec(node_data, parent_node=None):
if not isinstance(node_data, dict):
return
node_id = node_data.get('?', {}).get('id')
if not node_id:
return
node = node_refs[node_id]
atomics, containers = _split_seq_by_predicate(
six.iteritems(node_data), _is_atomic)
# the actual second pass of node linking
if parent_node is not None:
node['required_by'].append(parent_node['?']['id'])
node['link_type'] = 'aggregation'
for key, value in atomics:
if value in node_refs:
remote_node = node_refs[value]
if node_id not in remote_node['required_by']:
remote_node['required_by'].append(node_id)
remote_node['link_type'] = 'reference'
for key, value in containers:
if key == '?':
continue
if isinstance(value, dict):
build_links_rec(value, node_data)
elif isinstance(value, list):
for val in value:
build_links_rec(val, node_data)
for service in environment.services:
in_progress, status_message = _get_environment_status_message(service)
required_by = None
if 'instance' in service and service['instance'] is not None:
if service['instance'].get('assignFloatingIp', False):
if ext_net_name:
required_by = ext_net_name
else:
ext_net_name = 'External_Network'
ext_network_node = _create_ext_network_node(ext_net_name)
d3_data['nodes'].append(ext_network_node)
required_by = ext_net_name
service_node = _create_empty_node()
service_image = get_app_image(request, service['?']['type'],
service['?']['status'])
node_id = service['?']['id']
node_refs[node_id] = service_node
service_node.update({
'name': service.get('name', ''),
'status': status_message,
'image': service_image,
'id': node_id,
'link_type': 'relation',
'in_progress': in_progress,
'info_box': _application_info(
service, service_image, status_message)
})
if required_by:
service_node['required_by'].append(required_by)
d3_data['nodes'].append(service_node)
rec(service, None, service)
for service in environment.services:
build_links_rec(service)
return json.dumps(d3_data)