2bd4afb2bf
This script builds UML graphs taking Murano DSL manifests as input. It is able to build the following graphs: * single class inheritance tree * class inheritance tree with all resources, required by that and other classes Change-Id: Iac40f4524809e6637662c8db450816d57e7065f1
226 lines
7.0 KiB
Python
Executable File
226 lines
7.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
# Copyright (c) 2013 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 argparse
|
|
import os
|
|
import re
|
|
import yaml
|
|
|
|
|
|
# Workaround for unknown yaml tags
|
|
def yaml_default_ctor(loader, tag_suffix, node):
|
|
return tag_suffix + ' ' + node.value
|
|
|
|
yaml.add_multi_constructor('', yaml_default_ctor)
|
|
|
|
|
|
class PlantUmlNode():
|
|
def __init__(self, node_dn):
|
|
self._dn = node_dn
|
|
self.attributes = []
|
|
self.operations = []
|
|
self.extras = []
|
|
|
|
def write(self, f):
|
|
f.write('class {0} {{\n'.format(self._dn))
|
|
f.write('-- Properties --\n')
|
|
for item in self.attributes:
|
|
f.write('<{type}> {name}: {contract}\n'.format(**item))
|
|
f.write('-- Workflows --\n')
|
|
for item in self.operations:
|
|
f.write('{name}()\n'.format(**item))
|
|
f.write('-- Namespaces --\n')
|
|
for item in self.extras:
|
|
if item['key'] != '=':
|
|
f.write('{key}: {value}\n'.format(**item))
|
|
f.write('}\n')
|
|
|
|
def add_attribute(self, name, type, contract):
|
|
d = {'type': type, 'name': name, 'contract': contract}
|
|
self.attributes.append(d)
|
|
|
|
def add_operation(self, name):
|
|
d = {'name': name}
|
|
self.operations.append(d)
|
|
|
|
def add_extra(self, key, value):
|
|
d = {'key': key, 'value': value}
|
|
self.extras.append(d)
|
|
|
|
|
|
class DslSpec():
|
|
def __init__(self, class_dn, basepath='.'):
|
|
self._dn = class_dn
|
|
self._id = self._dn.replace('.', '_')
|
|
self._name = self._dn.split('.')[-1]
|
|
self._ns = ('=', self._dn.split('.')[:-1])
|
|
self._graph = None
|
|
self.is_virtual = True
|
|
self.parent_dn = None
|
|
self.manifest = None
|
|
|
|
manifest_path = os.path.join(basepath, self._dn, 'manifest.yaml')
|
|
if os.path.exists(manifest_path):
|
|
self.manifest = yaml.load(open(manifest_path))
|
|
|
|
if self.manifest:
|
|
self.is_virtual = False
|
|
self.name = self.manifest['Name']
|
|
if 'Extends' in self.manifest:
|
|
self.parent_dn = self.get_dn(self.manifest['Extends'])
|
|
|
|
def split_ns(self, name=None):
|
|
if name:
|
|
parts = name.split(':')
|
|
if len(parts) == 1:
|
|
parts.insert(0, '=')
|
|
else:
|
|
parts = ['=', self._name]
|
|
return parts
|
|
|
|
def get_ns(self, name=None):
|
|
parts = self.split_ns(name)
|
|
return {
|
|
'key': parts[0],
|
|
'value': self.manifest['Namespaces'][parts[0]]
|
|
}
|
|
|
|
def get_dn(self, name=None):
|
|
parts = self.split_ns(name)
|
|
parts[0] = self.get_ns(parts[0] + ':')['value']
|
|
return '.'.join(parts)
|
|
|
|
def get_name(self):
|
|
return self._name
|
|
|
|
|
|
class DslPlantUmlNode(DslSpec):
|
|
def write(self, file):
|
|
uml_class = PlantUmlNode(self._dn)
|
|
ext_classes = []
|
|
if not self.is_virtual:
|
|
namespaces = [self.get_ns()]
|
|
for name, item in self.manifest.get('Properties', {}).iteritems():
|
|
item_type = item.get('Type', 'In')
|
|
item_contract = str(item.get('Contract', 'UNDEFINED'))
|
|
match = re.search('class\((.*?)\)', item_contract)
|
|
if match:
|
|
ns = self.get_ns(match.group(1))
|
|
ext_classes.append(self.get_dn(match.group(1)))
|
|
if not ns in namespaces:
|
|
namespaces.append(ns)
|
|
uml_class.add_attribute(
|
|
name,
|
|
type=item_type,
|
|
contract=item_contract
|
|
)
|
|
for m in self.manifest.get('Workflow', []):
|
|
uml_class.add_operation(m)
|
|
for ns in namespaces:
|
|
uml_class.add_extra(ns['key'], ns['value'])
|
|
uml_class.write(file)
|
|
return ext_classes
|
|
|
|
|
|
class DslPlantUmlGraph():
|
|
def __init__(self):
|
|
self._nodes = []
|
|
self._edges = []
|
|
self._options = {}
|
|
self._file = None
|
|
|
|
def write(self, classname, level=0):
|
|
if level == 0:
|
|
self._file.write('@startuml\n')
|
|
if self.get_option('NoNamespaces', False):
|
|
self._file.write('set namespaceSeparator none\n')
|
|
|
|
if self.node_exists(classname):
|
|
return
|
|
|
|
self.add_node(classname)
|
|
|
|
node = DslPlantUmlNode(classname)
|
|
ext_classes = node.write(self._file)
|
|
|
|
if not self.get_option('ParentsOnly', False):
|
|
for ext_class in ext_classes:
|
|
self.add_edge(
|
|
from_node=ext_class,
|
|
to_node=classname,
|
|
edge_type='<..'
|
|
)
|
|
self.write(ext_class, level + 1)
|
|
|
|
if node.is_virtual:
|
|
return
|
|
|
|
if node.parent_dn:
|
|
self.add_edge(
|
|
from_node=node.parent_dn,
|
|
to_node=classname,
|
|
edge_type='<|--'
|
|
)
|
|
self.write(node.parent_dn, level + 1)
|
|
|
|
if level == 0:
|
|
for edge in self._edges:
|
|
self._file.write('{from_node} {type} {to_node}\n'
|
|
.format(**edge))
|
|
self._file.write('@enduml\n')
|
|
self._file.close()
|
|
|
|
def add_node(self, classname):
|
|
self._nodes.append(classname)
|
|
|
|
def node_exists(self, dn):
|
|
return dn in self._nodes
|
|
|
|
def add_edge(self, from_node, to_node, edge_type):
|
|
edge = {'from_node': from_node, 'to_node': to_node, 'type': edge_type}
|
|
if not edge in self._edges:
|
|
self._edges.append(edge)
|
|
|
|
def set_option(self, key, value):
|
|
self._options[key] = value
|
|
|
|
def get_option(self, key, default=None):
|
|
return self._options.get(key, default)
|
|
|
|
def open_file(self, file_name):
|
|
self._file = open(file_name, 'w')
|
|
|
|
def close_file(self):
|
|
self._file.close()
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Qwerty')
|
|
parser.add_argument('classname',
|
|
default='com.mirantis.murano.demoApp.DemoHost',
|
|
help='Dsl Class Name to draw.', nargs='?')
|
|
parser.add_argument('-n', '--no-namespaces', action='store_true')
|
|
parser.add_argument('-p', '--parents-only', action='store_true')
|
|
args = parser.parse_args()
|
|
|
|
graph = DslPlantUmlGraph()
|
|
graph.set_option('NoNamespaces', args.no_namespaces)
|
|
graph.set_option('ParentsOnly', args.parents_only)
|
|
graph.open_file('plantuml.txt')
|
|
graph.write(args.classname)
|
|
graph.close_file()
|
|
|
|
os.system('java -jar plantuml.jar plantuml.txt')
|
|
os.system('xdg-open plantuml.png')
|