mirror of
https://github.com/ansible/awx.git
synced 2026-04-23 18:55:24 -02:30
Updates pipeline and FSM design and development tools
* Updates pipeline and FSM design for 3.4 features:
group and read/write design features.
* Adds tool to copy layout from existing design
* Adds pipeline design
This commit is contained in:
1
awx/ui/client/src/network-ui/.gitignore
vendored
Normal file
1
awx/ui/client/src/network-ui/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/extracted
|
||||
19
awx/ui/client/src/network-ui/Makefile
Normal file
19
awx/ui/client/src/network-ui/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
.PHONY: check extract
|
||||
|
||||
FSMS = animation time test mode buttons button toolbox site rack group stream link details.panel move device.detail view keybindings hotkeys null
|
||||
|
||||
|
||||
extract:
|
||||
mkdir -p extracted
|
||||
for fsm in $(FSMS); do \
|
||||
./extract.js ./$${fsm}.fsm.js > extracted/$${fsm}.yml; \
|
||||
done
|
||||
|
||||
|
||||
check: extract
|
||||
for fsm in $(FSMS); do \
|
||||
./tools/fsm-diff ../../../../network_ui/designs/$$fsm.yml extracted/$$fsm.yml; \
|
||||
./tools/copy-layout.py ../../../../network_ui/designs/$$fsm.yml extracted/$$fsm.yml; \
|
||||
done
|
||||
47
awx/ui/client/src/network-ui/extract.js
Executable file
47
awx/ui/client/src/network-ui/extract.js
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env node
|
||||
var YAML = require('yamljs');
|
||||
|
||||
function Iterator(o){
|
||||
var k=Object.keys(o);
|
||||
return {
|
||||
next:function(){
|
||||
return k.shift();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var myArgs = process.argv.slice(2);
|
||||
var implementation = require(myArgs[0]);
|
||||
var states = [];
|
||||
var transitions = [];
|
||||
var data = {states: states,
|
||||
transitions: transitions};
|
||||
|
||||
var state_iter = Iterator(implementation);
|
||||
var transition_iter = null;
|
||||
var next_state = state_iter.next();
|
||||
var next_transition = null;
|
||||
var state = null;
|
||||
var transition = null;
|
||||
var i = 0;
|
||||
while(next_state !== undefined) {
|
||||
state = implementation[next_state];
|
||||
transition_iter = Iterator(state.constructor.prototype);
|
||||
next_transition = transition_iter.next();
|
||||
while (next_transition !== undefined) {
|
||||
transition = state.constructor.prototype[next_transition];
|
||||
if (transition.transitions !== undefined) {
|
||||
for (i = 0; i < transition.transitions.length; i++) {
|
||||
transitions.push({from_state: next_state,
|
||||
to_state:transition.transitions[i],
|
||||
label:next_transition});
|
||||
}
|
||||
}
|
||||
next_transition = transition_iter.next();
|
||||
}
|
||||
states.push({label: state.name});
|
||||
next_state = state_iter.next();
|
||||
}
|
||||
|
||||
|
||||
console.log(YAML.stringify(data));
|
||||
57
awx/ui/client/src/network-ui/extract_messages.js
Executable file
57
awx/ui/client/src/network-ui/extract_messages.js
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
var YAML = require('yamljs');
|
||||
|
||||
function Iterator(o){
|
||||
var k=Object.keys(o);
|
||||
return {
|
||||
next:function(){
|
||||
return k.shift();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var myArgs = process.argv.slice(2);
|
||||
var implementation = require(myArgs[0]);
|
||||
var messages = [];
|
||||
var data = {messages: messages};
|
||||
var message_iter = Iterator(implementation);
|
||||
var field_iter = null;
|
||||
var next_message = message_iter.next();
|
||||
var next_field = null;
|
||||
var message = null;
|
||||
var message_instance = null;
|
||||
var fields = null;
|
||||
// var field = null;
|
||||
// var i = 0;
|
||||
while(next_message !== undefined) {
|
||||
message = implementation[next_message];
|
||||
try {
|
||||
message_instance = new message();
|
||||
} catch(err) {
|
||||
next_message = message_iter.next();
|
||||
continue;
|
||||
}
|
||||
fields = [];
|
||||
field_iter = Iterator(message_instance);
|
||||
next_field = field_iter.next();
|
||||
while (next_field !== undefined) {
|
||||
fields.push(next_field);
|
||||
// field = message.constructor.prototype[next_field];
|
||||
// if (field.transitions !== undefined) {
|
||||
// for (i = 0; i < field.transitions.length; i++) {
|
||||
// transitions.push({from_message: next_message,
|
||||
// to_message:field.transitions[i],
|
||||
// label:next_field});
|
||||
// }
|
||||
// }
|
||||
next_field = field_iter.next();
|
||||
}
|
||||
if(message_instance.msg_type !== null && message_instance.msg_type !== undefined) {
|
||||
messages.push({msg_type: message_instance.msg_type,
|
||||
fields: fields});
|
||||
}
|
||||
next_message = message_iter.next();
|
||||
}
|
||||
|
||||
|
||||
console.log(YAML.stringify(data));
|
||||
@@ -1,8 +1,8 @@
|
||||
/* Copyright (c) 2017 Red Hat, Inc. */
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
var move = require('./move.js');
|
||||
var group = require('./group.js');
|
||||
var move = require('./move.fsm.js');
|
||||
var group = require('./group.fsm.js');
|
||||
var rack_fsm = require('./rack.fsm.js');
|
||||
var site_fsm = require('./site.fsm.js');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Copyright (c) 2017-2018 Red Hat, Inc. */
|
||||
var fsm = require('./fsm.js');
|
||||
var button = require('./button.js');
|
||||
var button = require('./button.fsm.js');
|
||||
var util = require('./util.js');
|
||||
var inherits = require('inherits');
|
||||
var animation_fsm = require('./animation.fsm.js');
|
||||
|
||||
@@ -8,13 +8,13 @@ var rack_fsm = require('./rack.fsm.js');
|
||||
var site_fsm = require('./site.fsm.js');
|
||||
var hotkeys = require('./hotkeys.fsm.js');
|
||||
var toolbox_fsm = require('./toolbox.fsm.js');
|
||||
var view = require('./view.js');
|
||||
var move = require('./move.js');
|
||||
var link = require('./link.js');
|
||||
var view = require('./view.fsm.js');
|
||||
var move = require('./move.fsm.js');
|
||||
var link = require('./link.fsm.js');
|
||||
var stream_fsm = require('./stream.fsm.js');
|
||||
var group = require('./group.js');
|
||||
var buttons = require('./buttons.js');
|
||||
var time = require('./time.js');
|
||||
var group = require('./group.fsm.js');
|
||||
var buttons = require('./buttons.fsm.js');
|
||||
var time = require('./time.fsm.js');
|
||||
var test_fsm = require('./test.fsm.js');
|
||||
var util = require('./util.js');
|
||||
var models = require('./models.js');
|
||||
|
||||
26
awx/ui/client/src/network-ui/templates/fsm.jst
Normal file
26
awx/ui/client/src/network-ui/templates/fsm.jst
Normal file
@@ -0,0 +1,26 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
{%for state in states%}
|
||||
function _{{state.label}} () {
|
||||
this.name = '{{state.label}}';
|
||||
}
|
||||
inherits(_{{state.label}}, _State);
|
||||
var {{state.label}} = new _{{state.label}}();
|
||||
exports.{{state.label}} = {{state.label}};
|
||||
{%endfor%}
|
||||
|
||||
{%for state in states%}
|
||||
{%for fn, transitions in state.functions%}
|
||||
_{{state.label}}.prototype.{{fn}} = function (controller) {
|
||||
{%for tn in transitions %}
|
||||
controller.changeState({{tn.to_state}});
|
||||
{%endfor%}
|
||||
};
|
||||
_{{state.label}}.prototype.{{fn}}.transitions = [{%for t in transitions%}'{{t.to_state}}'{% if not loop.last%}, {%endif%}{%endfor%}];
|
||||
{%endfor%}
|
||||
{%endfor%}
|
||||
55
awx/ui/client/src/network-ui/tools/copy-layout.py
Executable file
55
awx/ui/client/src/network-ui/tools/copy-layout.py
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Benjamin Thomasson
|
||||
|
||||
"""
|
||||
Usage:
|
||||
copy-layout [options] <from> <to>
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger('copy-layout')
|
||||
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
parsed_args = docopt(__doc__, args)
|
||||
if parsed_args['--debug']:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif parsed_args['--verbose']:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
with open(parsed_args['<from>']) as f:
|
||||
from_fsm = yaml.load(f.read())
|
||||
with open(parsed_args['<to>']) as f:
|
||||
to_fsm = yaml.load(f.read())
|
||||
|
||||
to_states = {x['label']: x for x in to_fsm.get('states', [])}
|
||||
|
||||
to_fsm['name'] = from_fsm.get('name', '')
|
||||
to_fsm['finite_state_machine_id'] = from_fsm.get('finite_state_machine_id', '')
|
||||
to_fsm['diagram_id'] = from_fsm.get('diagram_id', '')
|
||||
|
||||
for state in from_fsm.get('states', []):
|
||||
to_states.get(state['label'], {})['x'] = state.get('x', 0)
|
||||
to_states.get(state['label'], {})['y'] = state.get('y', 0)
|
||||
|
||||
with open(parsed_args['<to>'], 'w') as f:
|
||||
f.write(yaml.safe_dump(to_fsm, default_flow_style=False))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
9
awx/ui/client/src/network-ui/tools/fsm-diff
Executable file
9
awx/ui/client/src/network-ui/tools/fsm-diff
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fsm_diff.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
6
awx/ui/client/src/network-ui/tools/fsm_diff/__init__.py
Executable file
6
awx/ui/client/src/network-ui/tools/fsm_diff/__init__.py
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__author__ = 'Ben Thomasson'
|
||||
__email__ = 'benthomasson@gmail.com'
|
||||
__version__ = '0.1.0'
|
||||
79
awx/ui/client/src/network-ui/tools/fsm_diff/cli.py
Normal file
79
awx/ui/client/src/network-ui/tools/fsm_diff/cli.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Usage:
|
||||
fsm_diff [options] <a> <b> [<output>]
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger('cli')
|
||||
|
||||
|
||||
def fsm_diff(a_name, b_name, a, b, silent=True):
|
||||
|
||||
a_states = {x['label'] for x in a['states']}
|
||||
b_states = {x['label'] for x in b['states']}
|
||||
|
||||
missing_in_a = b_states - a_states
|
||||
missing_in_b = a_states - b_states
|
||||
|
||||
|
||||
if (missing_in_b) and not silent:
|
||||
print "Extra states in " + a_name + ":\n ", "\n ".join(list(missing_in_b))
|
||||
|
||||
if (missing_in_a) and not silent:
|
||||
print "Extra states in " + b_name + ":\n ", "\n ".join(list(missing_in_a))
|
||||
|
||||
new_states = missing_in_b.union(missing_in_a)
|
||||
|
||||
a_transitions = {tuple(sorted(x.items())) for x in a['transitions']}
|
||||
b_transitions = {tuple(sorted(x.items())) for x in b['transitions']}
|
||||
|
||||
missing_in_a = b_transitions - a_transitions
|
||||
missing_in_b = a_transitions - b_transitions
|
||||
|
||||
|
||||
if (missing_in_b) and not silent:
|
||||
print "Extra transitions in " + a_name + ":\n ", "\n ".join(map(str, missing_in_b))
|
||||
|
||||
if (missing_in_a) and not silent:
|
||||
print "Extra transitions in " + b_name + ":\n ", "\n ".join(map(str, missing_in_a))
|
||||
|
||||
new_transitions = missing_in_b.union(missing_in_a)
|
||||
|
||||
data = dict(states=[dict(label=x) for x in list(new_states)],
|
||||
transitions=[dict(x) for x in list(new_transitions)])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
parsed_args = docopt(__doc__, args)
|
||||
if parsed_args['--debug']:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif parsed_args['--verbose']:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
with open(parsed_args['<a>']) as f:
|
||||
a = yaml.load(f.read())
|
||||
|
||||
with open(parsed_args['<b>']) as f:
|
||||
b = yaml.load(f.read())
|
||||
|
||||
data = fsm_diff(parsed_args['<a>'], parsed_args['<b>'], a, b, silent=False)
|
||||
|
||||
if parsed_args['<output>']:
|
||||
with open(parsed_args['<output>'], 'w') as f:
|
||||
f.write(yaml.dump(data, default_flow_style=False))
|
||||
|
||||
return 0
|
||||
70
awx/ui/client/src/network-ui/tools/fsm_generate_diffs.py
Executable file
70
awx/ui/client/src/network-ui/tools/fsm_generate_diffs.py
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017 Red Hat, Inc
|
||||
|
||||
"""
|
||||
Usage:
|
||||
fsm_generate_diffs [options] <design> <implementation>
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
--append Append the newly generated code to the implementation.
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import fsm_diff.cli
|
||||
import transform_fsm
|
||||
import yaml
|
||||
|
||||
from jinja2 import FileSystemLoader, Environment
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
logger = logging.getLogger('fsm_generate_diffs')
|
||||
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
parsed_args = docopt(__doc__, args)
|
||||
if parsed_args['--debug']:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif parsed_args['--verbose']:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
implementation = parsed_args['<implementation>']
|
||||
|
||||
p = Popen(['./extract.js', implementation], stdout=PIPE)
|
||||
output = p.communicate()[0]
|
||||
if p.returncode == 0:
|
||||
b = yaml.load(output)
|
||||
else:
|
||||
return 1
|
||||
|
||||
with open(parsed_args['<design>']) as f:
|
||||
a = yaml.load(f.read())
|
||||
|
||||
data = fsm_diff.cli.fsm_diff(a, b)
|
||||
data = transform_fsm.transform_fsm(data)
|
||||
|
||||
env = Environment(loader=FileSystemLoader("templates"))
|
||||
template = env.get_template('fsm.jst')
|
||||
|
||||
if parsed_args['--append']:
|
||||
with open(implementation, "a") as f:
|
||||
f.write(template.render(**data))
|
||||
else:
|
||||
print (template.render(**data))
|
||||
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
4
awx/ui/client/src/network-ui/tools/requirements.txt
Normal file
4
awx/ui/client/src/network-ui/tools/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
jinja2
|
||||
docopt
|
||||
pyyaml
|
||||
|
||||
68
awx/ui/client/src/network-ui/tools/transform_fsm.py
Executable file
68
awx/ui/client/src/network-ui/tools/transform_fsm.py
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017 Red Hat, Inc
|
||||
|
||||
"""
|
||||
Usage:
|
||||
transform_fsm [options] <input> <output>
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger('transform_fsm')
|
||||
|
||||
|
||||
def transform_fsm(data):
|
||||
|
||||
state_map = dict()
|
||||
|
||||
for state in data['states']:
|
||||
state_map[state['label']] = state
|
||||
state['functions'] = dict()
|
||||
|
||||
for transition in data['transitions']:
|
||||
state = state_map.get(transition['from_state'], dict(label=transition['from_state'], functions=dict()))
|
||||
state_map[transition['from_state']] = state
|
||||
if state not in data['states']:
|
||||
data['states'].append(state)
|
||||
function_transitions = state['functions'].get(transition['label'], list())
|
||||
function_transitions.append(dict(to_state=transition['to_state']))
|
||||
state['functions'][transition['label']] = function_transitions
|
||||
|
||||
for state in data['states']:
|
||||
state['functions'] = sorted(state['functions'].items())
|
||||
|
||||
return data
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
parsed_args = docopt(__doc__, args)
|
||||
if parsed_args['--debug']:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
elif parsed_args['--verbose']:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
with open(parsed_args['<input>']) as f:
|
||||
data = yaml.load(f.read())
|
||||
|
||||
data = transform_fsm(data)
|
||||
|
||||
with open(parsed_args['<output>'], 'w') as f:
|
||||
f.write(yaml.safe_dump(data, default_flow_style=False))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
Reference in New Issue
Block a user