Merge "UML Generator script"
This commit is contained in:
commit
29ae351a7d
28
contrib/utils/uml-generator/README.md
Normal file
28
contrib/utils/uml-generator/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# MuranoPL UML Generator
|
||||
|
||||
This folder contains scripts (currently only one) to generate UML graphs based on MuranoPL manifests.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy **umlgen.py** to **meta** folder under MuranoPL directory.
|
||||
|
||||
2. Download **plantuml.jar** from http://plantuml.sourceforge.net/ and copy it to the folder ablve.
|
||||
|
||||
3. Generate UML graph using the command below:
|
||||
|
||||
```
|
||||
./umlgen.py
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
./umlgen.py [--no-namespaces] [--parents-only] [CLASS_FQDN]
|
||||
```
|
||||
|
||||
* **--no-namespaces** - disables automatic classes grouping
|
||||
|
||||
* **--parents-only** - generate graph using only parent-child dependencies
|
||||
|
||||
* **CLASS_FQDN** - MuranoPL class FQDN
|
||||
|
225
contrib/utils/uml-generator/umlgen.py
Executable file
225
contrib/utils/uml-generator/umlgen.py
Executable file
@ -0,0 +1,225 @@
|
||||
#!/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')
|
Loading…
Reference in New Issue
Block a user