mirror of
https://github.com/ansible/awx.git
synced 2026-01-18 13:11:19 -03:30
Adds network UI test framework
This adds a test framework to drive UI tests from the client instead of injecting events from the websocket. Tests consist of a pair of snapshots (before and after the test) and a list of UI events to process. Tests are run using a FSM in the client that controls the resetting of state to the snapshot, injecting the events into the UI, recording test coverage, and reporting tests to the server. * Adds design for event trace table * Adds design for a coverage tracking table * Adds models for EventTrace and Coverage * Adds trace_id to recording messages * Adds design for TopologySnapshot table * Adds order to TopologySnapshot table * Adds TopologySnapshot table * Adds Snapshot message when recordings are started and stoppped * Adds models for tracking test cases and test results * Adds designs for a test runner FSM * Updates test management commands with new schema * Adds download recording button * Adds models to track tests * Adds ui test runner * Adds id and client to TestResult design * Adds id and client to TestResult * Update message types * Stores test results and code coverage from the test runner * Adds tool to generate a test coverage report * Adds APIs for tests and code coverage * Adds per-test-case coverage reports * Breaks out coverage for loading the modules from the tests * Re-raises server-side errors * Captures errors during tests * Adds defaults for host name and host type * Disables test FSM trace storage * Adds support for sending server error message to the client * Resets the UI flags, history, and toolbox contents between tests * Adds istanbul instrumentation to network-ui
This commit is contained in:
parent
eeaf7c257c
commit
bf7f4ee1e1
@ -36,6 +36,20 @@ from awx.network_ui.models import FSMTrace
|
||||
|
||||
from awx.network_ui.models import TopologyInventory
|
||||
|
||||
from awx.network_ui.models import EventTrace
|
||||
|
||||
from awx.network_ui.models import Coverage
|
||||
|
||||
from awx.network_ui.models import TopologySnapshot
|
||||
|
||||
from awx.network_ui.models import TestCase
|
||||
|
||||
from awx.network_ui.models import Result
|
||||
|
||||
from awx.network_ui.models import CodeUnderTest
|
||||
|
||||
from awx.network_ui.models import TestResult
|
||||
|
||||
|
||||
class DeviceAdmin(admin.ModelAdmin):
|
||||
fields = ('topology', 'name', 'x', 'y', 'id', 'type', 'interface_id_seq', 'process_id_seq', 'host_id',)
|
||||
@ -179,3 +193,59 @@ class TopologyInventoryAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
admin.site.register(TopologyInventory, TopologyInventoryAdmin)
|
||||
|
||||
|
||||
class EventTraceAdmin(admin.ModelAdmin):
|
||||
fields = ('client', 'trace_session_id', 'event_data', 'message_id',)
|
||||
raw_id_fields = ('client',)
|
||||
|
||||
|
||||
admin.site.register(EventTrace, EventTraceAdmin)
|
||||
|
||||
|
||||
class CoverageAdmin(admin.ModelAdmin):
|
||||
fields = ('coverage_data', 'test_result',)
|
||||
raw_id_fields = ('test_result',)
|
||||
|
||||
|
||||
admin.site.register(Coverage, CoverageAdmin)
|
||||
|
||||
|
||||
class TopologySnapshotAdmin(admin.ModelAdmin):
|
||||
fields = ('client', 'topology_id', 'trace_session_id', 'snapshot_data', 'order',)
|
||||
raw_id_fields = ('client', 'snapshot_data',)
|
||||
|
||||
|
||||
admin.site.register(TopologySnapshot, TopologySnapshotAdmin)
|
||||
|
||||
|
||||
class TestCaseAdmin(admin.ModelAdmin):
|
||||
fields = ('name', 'test_case_data',)
|
||||
raw_id_fields = ('name',)
|
||||
|
||||
|
||||
admin.site.register(TestCase, TestCaseAdmin)
|
||||
|
||||
|
||||
class ResultAdmin(admin.ModelAdmin):
|
||||
fields = ('name',)
|
||||
raw_id_fields = ()
|
||||
|
||||
|
||||
admin.site.register(Result, ResultAdmin)
|
||||
|
||||
|
||||
class CodeUnderTestAdmin(admin.ModelAdmin):
|
||||
fields = ('version_x', 'version_y', 'version_z', 'commits_since', 'commit_hash',)
|
||||
raw_id_fields = ('code_under_test_id',)
|
||||
|
||||
|
||||
admin.site.register(CodeUnderTest, CodeUnderTestAdmin)
|
||||
|
||||
|
||||
class TestResultAdmin(admin.ModelAdmin):
|
||||
fields = ('test_case', 'result', 'code_under_test', 'time', 'id', 'client',)
|
||||
raw_id_fields = ('test_case', 'result', 'code_under_test', 'client',)
|
||||
|
||||
|
||||
admin.site.register(TestResult, TestResultAdmin)
|
||||
|
||||
@ -8,8 +8,9 @@ from awx.network_ui.models import GroupDevice as GroupDeviceMap
|
||||
from awx.network_ui.models import DataSheet, DataBinding, DataType
|
||||
from awx.network_ui.models import Process, Stream
|
||||
from awx.network_ui.models import Toolbox, ToolboxItem
|
||||
from awx.network_ui.models import FSMTrace
|
||||
from awx.network_ui.models import FSMTrace, EventTrace, Coverage, TopologySnapshot
|
||||
from awx.network_ui.models import TopologyInventory
|
||||
from awx.network_ui.models import TestCase, TestResult, CodeUnderTest, Result
|
||||
from awx.network_ui.messages import MultipleMessage, InterfaceCreate, LinkCreate, to_dict
|
||||
import urlparse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@ -18,14 +19,14 @@ from collections import defaultdict
|
||||
import math
|
||||
import random
|
||||
import logging
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
|
||||
from awx.network_ui.utils import transform_dict
|
||||
import dpath.util
|
||||
from pprint import pformat
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
# Connected to websocket.connect
|
||||
|
||||
HISTORY_MESSAGE_IGNORE_TYPES = ['DeviceSelected',
|
||||
@ -44,6 +45,10 @@ RACK_SPACING = 50
|
||||
|
||||
logger = logging.getLogger("awx.network_ui.consumers")
|
||||
|
||||
class NetworkUIException(Exception):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def circular_layout(topology_id):
|
||||
n = Device.objects.filter(topology_id=topology_id).count()
|
||||
@ -281,7 +286,7 @@ class _Persistence(object):
|
||||
try:
|
||||
message_type_id = MessageType.objects.get(name=message_type).pk
|
||||
except ObjectDoesNotExist:
|
||||
logger.warning("Unsupported message %s", message_type)
|
||||
logger.warning("Unsupported message %s: no message type", message_type)
|
||||
return
|
||||
TopologyHistory(topology_id=topology_id,
|
||||
client_id=client_id,
|
||||
@ -290,9 +295,19 @@ class _Persistence(object):
|
||||
message_data=message['text']).save()
|
||||
handler = self.get_handler(message_type)
|
||||
if handler is not None:
|
||||
handler(message_value, topology_id, client_id)
|
||||
try:
|
||||
handler(message_value, topology_id, client_id)
|
||||
except NetworkUIException, e:
|
||||
Group("client-%s" % client_id).send({"text": json.dumps(["Error", str(e)])})
|
||||
raise
|
||||
except Exception, e:
|
||||
Group("client-%s" % client_id).send({"text": json.dumps(["Error", "Server Error"])})
|
||||
raise
|
||||
except BaseException, e:
|
||||
Group("client-%s" % client_id).send({"text": json.dumps(["Error", "Server Error"])})
|
||||
raise
|
||||
else:
|
||||
logger.warning("Unsupported message %s", message_type)
|
||||
logger.warning("Unsupported message %s: no handler", message_type)
|
||||
|
||||
def get_handler(self, message_type):
|
||||
return getattr(self, "on{0}".format(message_type), None)
|
||||
@ -454,12 +469,44 @@ class _Persistence(object):
|
||||
# grid_layout(topology_id)
|
||||
tier_layout(topology_id)
|
||||
|
||||
|
||||
def onCoverageRequest(self, coverage, topology_id, client_id):
|
||||
pass
|
||||
|
||||
def onTestResult(self, test_result, topology_id, client_id):
|
||||
xyz, _, rest = test_result['code_under_test'].partition('-')
|
||||
commits_since, _, commit_hash = rest.partition('-')
|
||||
commit_hash = commit_hash.strip('g')
|
||||
|
||||
print (xyz)
|
||||
print (commits_since)
|
||||
print (commit_hash)
|
||||
|
||||
x, y, z = [int(i) for i in xyz.split('.')]
|
||||
|
||||
print (x, y, z)
|
||||
|
||||
code_under_test, _ = CodeUnderTest.objects.get_or_create(version_x=x,
|
||||
version_y=y,
|
||||
version_z=z,
|
||||
commits_since=int(commits_since),
|
||||
commit_hash=commit_hash)
|
||||
|
||||
print (code_under_test)
|
||||
|
||||
tr = TestResult(id=test_result['id'],
|
||||
result_id=Result.objects.get(name=test_result['result']).pk,
|
||||
test_case_id=TestCase.objects.get(name=test_result['name']).pk,
|
||||
code_under_test_id=code_under_test.pk,
|
||||
client_id=client_id,
|
||||
time=parse_datetime(test_result['date']))
|
||||
tr.save()
|
||||
print (tr.pk)
|
||||
|
||||
|
||||
def onCoverage(self, coverage, topology_id, client_id):
|
||||
with open(os.path.abspath("coverage/coverage{0}.json".format(int(time.time()))), "w") as f:
|
||||
f.write(json.dumps(coverage['coverage']))
|
||||
Coverage(test_result_id=TestResult.objects.get(id=coverage['result_id'], client_id=client_id).pk,
|
||||
coverage_data=json.dumps(coverage['coverage'])).save()
|
||||
|
||||
def onStartRecording(self, recording, topology_id, client_id):
|
||||
pass
|
||||
@ -469,9 +516,10 @@ class _Persistence(object):
|
||||
|
||||
def write_event(self, event, topology_id, client_id):
|
||||
if event.get('save', True):
|
||||
with open(os.path.abspath("recording/recording_{0}.log".format(topology_id)), "a") as f:
|
||||
f.write(json.dumps(event))
|
||||
f.write("\n")
|
||||
EventTrace(trace_session_id=event['trace_id'],
|
||||
event_data=json.dumps(event),
|
||||
message_id=event['message_id'],
|
||||
client_id=client_id).save()
|
||||
|
||||
onViewPort = write_event
|
||||
onMouseEvent = write_event
|
||||
@ -538,6 +586,13 @@ class _Persistence(object):
|
||||
client_id=client_id,
|
||||
message_type=message_value['recv_message_type'] or "none").save()
|
||||
|
||||
def onSnapshot(self, snapshot, topology_id, client_id):
|
||||
TopologySnapshot(trace_session_id=snapshot['trace_id'],
|
||||
snapshot_data=json.dumps(snapshot),
|
||||
order=snapshot['order'],
|
||||
client_id=client_id,
|
||||
topology_id=topology_id).save()
|
||||
|
||||
|
||||
persistence = _Persistence()
|
||||
|
||||
@ -554,7 +609,7 @@ class _UndoPersistence(object):
|
||||
if handler is not None:
|
||||
handler(message_value, topology_id, client_id)
|
||||
else:
|
||||
logger.warnding("Unsupported undo message %s", message_type)
|
||||
logger.warning("Unsupported undo message %s", message_type)
|
||||
|
||||
def onSnapshot(self, snapshot, topology_id, client_id):
|
||||
pass
|
||||
@ -648,7 +703,7 @@ class _Discovery(object):
|
||||
if handler is not None:
|
||||
handler(message_value, topology_id)
|
||||
else:
|
||||
logger.warning("Unsupported message %s", message_type)
|
||||
logger.warning("Unsupported discovery message %s", message_type)
|
||||
|
||||
def get_handler(self, message_type):
|
||||
return getattr(self, "on{0}".format(message_type), None)
|
||||
@ -810,6 +865,7 @@ def ws_connect(message):
|
||||
client = Client()
|
||||
client.save()
|
||||
message.channel_session['client_id'] = client.pk
|
||||
Group("client-%s" % client.pk).add(message.reply_channel)
|
||||
message.reply_channel.send({"text": json.dumps(["id", client.pk])})
|
||||
message.reply_channel.send({"text": json.dumps(["topology_id", topology_id])})
|
||||
topology_data = transform_dict(dict(topology_id='topology_id',
|
||||
@ -825,6 +881,7 @@ def ws_connect(message):
|
||||
send_snapshot(message.reply_channel, topology_id)
|
||||
send_history(message.reply_channel, topology_id)
|
||||
send_toolboxes(message.reply_channel)
|
||||
send_tests(message.reply_channel)
|
||||
|
||||
|
||||
def send_toolboxes(channel):
|
||||
@ -834,6 +891,11 @@ def send_toolboxes(channel):
|
||||
channel.send({"text": json.dumps(["ToolboxItem", item])})
|
||||
|
||||
|
||||
def send_tests(channel):
|
||||
for name, test_case_data in TestCase.objects.all().values_list('name', 'test_case_data'):
|
||||
channel.send({"text": json.dumps(["TestCase", [name, json.loads(test_case_data)]])})
|
||||
|
||||
|
||||
def send_snapshot(channel, topology_id):
|
||||
interfaces = defaultdict(list)
|
||||
processes = defaultdict(list)
|
||||
@ -919,7 +981,8 @@ def ws_message(message):
|
||||
|
||||
@channel_session
|
||||
def ws_disconnect(message):
|
||||
Group("topology-%s" % message.channel_session['topology_id']).discard(message.reply_channel)
|
||||
if 'topology_id' in message.channel_session:
|
||||
Group("topology-%s" % message.channel_session['topology_id']).discard(message.reply_channel)
|
||||
|
||||
|
||||
def console_printer(message):
|
||||
|
||||
@ -102,8 +102,8 @@ models:
|
||||
pk: true
|
||||
type: AutoField
|
||||
name: Client
|
||||
x: -510
|
||||
y: 141
|
||||
x: -518
|
||||
y: 138
|
||||
- fields:
|
||||
- name: topology_history_id
|
||||
pk: true
|
||||
@ -365,6 +365,130 @@ models:
|
||||
name: TopologyInventory
|
||||
x: -226
|
||||
y: -19
|
||||
- fields:
|
||||
- name: event_trace_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: client
|
||||
ref: Client
|
||||
ref_field: client_id
|
||||
type: ForeignKey
|
||||
- default: 0
|
||||
name: trace_session_id
|
||||
type: IntegerField
|
||||
- name: event_data
|
||||
type: TextField
|
||||
- name: message_id
|
||||
type: IntegerField
|
||||
name: EventTrace
|
||||
x: -1087
|
||||
y: 202
|
||||
- fields:
|
||||
- name: coverage_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: coverage_data
|
||||
type: TextField
|
||||
- name: test_result
|
||||
ref: TestResult
|
||||
ref_field: test_result_id
|
||||
type: ForeignKey
|
||||
name: Coverage
|
||||
x: -1068
|
||||
y: -4
|
||||
- fields:
|
||||
- name: topology_snapshot_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: client
|
||||
ref: Client
|
||||
ref_field: client_id
|
||||
type: ForeignKey
|
||||
- name: topology_id
|
||||
type: IntegerField
|
||||
- name: trace_session_id
|
||||
type: IntegerField
|
||||
- name: snapshot_data
|
||||
ref: TopologySnapshot
|
||||
ref_field: snapshot_data
|
||||
type: TextField
|
||||
- name: order
|
||||
type: IntegerField
|
||||
name: TopologySnapshot
|
||||
x: -1123
|
||||
y: -277
|
||||
- fields:
|
||||
- name: test_case_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- len: 200
|
||||
name: name
|
||||
ref: TestCase
|
||||
ref_field: name
|
||||
type: CharField
|
||||
- name: test_case_data
|
||||
type: TextField
|
||||
name: TestCase
|
||||
x: -1642
|
||||
y: -38
|
||||
- fields:
|
||||
- name: result_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- len: 20
|
||||
name: name
|
||||
type: CharField
|
||||
name: Result
|
||||
x: -1610
|
||||
y: 120
|
||||
- fields:
|
||||
- name: code_under_test_id
|
||||
pk: true
|
||||
ref: CodeUnderTest
|
||||
ref_field: code_under_test_id
|
||||
type: AutoField
|
||||
- name: version_x
|
||||
type: IntegerField
|
||||
- name: version_y
|
||||
type: IntegerField
|
||||
- name: version_z
|
||||
type: IntegerField
|
||||
- name: commits_since
|
||||
type: IntegerField
|
||||
- len: 40
|
||||
name: commit_hash
|
||||
type: CharField
|
||||
name: CodeUnderTest
|
||||
x: -1612
|
||||
y: 259
|
||||
- fields:
|
||||
- name: test_result_id
|
||||
pk: true
|
||||
type: AutoField
|
||||
- name: test_case
|
||||
ref: TestCase
|
||||
ref_field: test_case_id
|
||||
type: ForeignKey
|
||||
- name: result
|
||||
ref: Result
|
||||
ref_field: result_id
|
||||
type: ForeignKey
|
||||
- name: code_under_test
|
||||
ref: CodeUnderTest
|
||||
ref_field: code_under_test_id
|
||||
type: ForeignKey
|
||||
- name: time
|
||||
type: DateTimeField
|
||||
- default: 0
|
||||
name: id
|
||||
type: IntegerField
|
||||
- name: client
|
||||
ref: Client
|
||||
ref_field: client_id
|
||||
type: ForeignKey
|
||||
name: TestResult
|
||||
x: -1336
|
||||
y: -49
|
||||
modules: []
|
||||
view:
|
||||
panX: 213.729555519212
|
||||
|
||||
@ -3,26 +3,22 @@ from django.core.management.base import BaseCommand
|
||||
from awx.network_ui.models import Topology, Device, Link, Interface
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def natural_numbers():
|
||||
i = 1
|
||||
while True:
|
||||
yield i
|
||||
i += 1
|
||||
from .util import natural_numbers
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '''Creates a 2 tier clos topology with n nodes in the 1st tier and m nodes
|
||||
in the 2nd tier and h hosts per pair of switches'''
|
||||
help = '''Adds a 2 tier clos topology with n nodes in the 1st tier and m nodes
|
||||
in the 2nd tier and h hosts per pair of switches to the topology with id `id`'''
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('id', type=int)
|
||||
parser.add_argument('n', type=int)
|
||||
parser.add_argument('m', type=int)
|
||||
parser.add_argument('h', type=int)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
topology_id = options['id']
|
||||
n = options['n']
|
||||
m = options['m']
|
||||
h = options['h']
|
||||
@ -30,15 +26,15 @@ class Command(BaseCommand):
|
||||
print "n", n
|
||||
print "m", m
|
||||
|
||||
topology = Topology(name="test_{0}".format(n), scale=1.0, panX=0, panY=0)
|
||||
topology.save()
|
||||
topology = Topology.objects.get(pk=topology_id)
|
||||
|
||||
devices = []
|
||||
hosts_per_leaf = []
|
||||
leaves = []
|
||||
spines = []
|
||||
|
||||
id_seq = natural_numbers()
|
||||
id_seq = natural_numbers(topology.device_id_seq)
|
||||
link_id_seq = natural_numbers(topology.link_id_seq)
|
||||
|
||||
tier2 = 100
|
||||
tier1 = 500
|
||||
@ -106,7 +102,8 @@ class Command(BaseCommand):
|
||||
link = Link(from_device=devices[leaf.id],
|
||||
to_device=devices[spine.id],
|
||||
from_interface=from_interface,
|
||||
to_interface=to_interface)
|
||||
to_interface=to_interface,
|
||||
id=next(link_id_seq))
|
||||
links.append(link)
|
||||
for i, hosts in enumerate(hosts_per_leaf):
|
||||
leaf1 = leaves[2 * i]
|
||||
@ -125,7 +122,8 @@ class Command(BaseCommand):
|
||||
link = Link(from_device=devices[leaf1.id],
|
||||
to_device=devices[host.id],
|
||||
from_interface=from_interface,
|
||||
to_interface=to_interface)
|
||||
to_interface=to_interface,
|
||||
id=next(link_id_seq))
|
||||
links.append(link)
|
||||
from_interface = Interface(device=devices[leaf2.id],
|
||||
name="swp" + str(len(interfaces[leaf2.id]) + 1),
|
||||
@ -140,9 +138,14 @@ class Command(BaseCommand):
|
||||
link = Link(from_device=devices[leaf2.id],
|
||||
to_device=devices[host.id],
|
||||
from_interface=from_interface,
|
||||
to_interface=to_interface)
|
||||
to_interface=to_interface,
|
||||
id=next(link_id_seq))
|
||||
links.append(link)
|
||||
|
||||
Link.objects.bulk_create(links)
|
||||
|
||||
topology.device_id_seq = next(id_seq)
|
||||
topology.link_id_seq = next(link_id_seq)
|
||||
topology.save()
|
||||
|
||||
print "Topology: ", topology.pk
|
||||
|
||||
@ -1,24 +1,29 @@
|
||||
# Copyright (c) 2017 Red Hat, Inc
|
||||
from django.core.management.base import BaseCommand
|
||||
from awx.network_ui.models import Topology, Device, Link
|
||||
from awx.network_ui.models import Topology, Device, Link, Interface
|
||||
|
||||
import math
|
||||
|
||||
from collections import defaultdict
|
||||
from .util import natural_numbers
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Creates a fully connected topology with n nodes'
|
||||
help = 'Adds a fully connected topology with n nodes to topology pk id'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('id', type=int)
|
||||
parser.add_argument('n', type=int)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
topology_id = options['id']
|
||||
n = options['n']
|
||||
|
||||
topology = Topology(name="test_{0}".format(n), scale=1.0, panX=0, panY=0)
|
||||
topology.save()
|
||||
topology = Topology.objects.get(topology_id=topology_id)
|
||||
|
||||
link_id_seq = natural_numbers(topology.link_id_seq)
|
||||
device_id_seq = natural_numbers(topology.device_id_seq)
|
||||
devices = []
|
||||
|
||||
r = 1000
|
||||
@ -30,25 +35,39 @@ class Command(BaseCommand):
|
||||
|
||||
for i in xrange(n):
|
||||
device = Device(name="R{0}".format(i),
|
||||
x=math.cos(arc_radians*i)*r,
|
||||
y=math.sin(arc_radians*i)*r,
|
||||
id=i,
|
||||
x=math.cos(arc_radians * i) * r,
|
||||
y=math.sin(arc_radians * i) * r,
|
||||
id=next(device_id_seq),
|
||||
type="router",
|
||||
topology_id=topology.pk)
|
||||
devices.append(device)
|
||||
|
||||
Device.objects.bulk_create(devices)
|
||||
|
||||
devices = {x.id: x for x in Device.objects.filter(topology_id=topology.pk)}
|
||||
devices = list(Device.objects.filter(topology_id=topology.pk))
|
||||
|
||||
links = []
|
||||
interfaces = defaultdict(list)
|
||||
|
||||
for i in xrange(n):
|
||||
for j in xrange(i):
|
||||
if i == j:
|
||||
continue
|
||||
from_interface = Interface(device=devices[i],
|
||||
name="swp" + str(len(interfaces[i]) + 1),
|
||||
id=(len(interfaces[i]) + 1))
|
||||
from_interface.save()
|
||||
interfaces[i].append(from_interface)
|
||||
to_interface = Interface(device=devices[j],
|
||||
name="swp" + str(len(interfaces[j]) + 1),
|
||||
id=(len(interfaces[j]) + 1))
|
||||
to_interface.save()
|
||||
interfaces[j].append(to_interface)
|
||||
link = Link(from_device=devices[i],
|
||||
to_device=devices[j])
|
||||
to_device=devices[j],
|
||||
from_interface=from_interface,
|
||||
to_interface=to_interface,
|
||||
id=next(link_id_seq))
|
||||
links.append(link)
|
||||
Link.objects.bulk_create(links)
|
||||
|
||||
|
||||
5
awx/network_ui/management/commands/util.py
Normal file
5
awx/network_ui/management/commands/util.py
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
def natural_numbers(i=1):
|
||||
while True:
|
||||
yield i
|
||||
i += 1
|
||||
24
awx/network_ui/migrations/0027_eventtrace.py
Normal file
24
awx/network_ui/migrations/0027_eventtrace.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0026_auto_20180105_1403'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EventTrace',
|
||||
fields=[
|
||||
('event_trace_id', models.AutoField(serialize=False, primary_key=True)),
|
||||
('trace_session_id', models.IntegerField(default=0)),
|
||||
('event_data', models.TextField()),
|
||||
('message_id', models.IntegerField()),
|
||||
('client', models.ForeignKey(to='network_ui.Client')),
|
||||
],
|
||||
),
|
||||
]
|
||||
23
awx/network_ui/migrations/0028_coverage.py
Normal file
23
awx/network_ui/migrations/0028_coverage.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0027_eventtrace'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Coverage',
|
||||
fields=[
|
||||
('coverage_id', models.AutoField(serialize=False, primary_key=True)),
|
||||
('trace_session_id', models.IntegerField()),
|
||||
('coverage_data', models.TextField()),
|
||||
('client', models.ForeignKey(to='network_ui.Client')),
|
||||
],
|
||||
),
|
||||
]
|
||||
27
awx/network_ui/migrations/0029_topologysnapshot.py
Normal file
27
awx/network_ui/migrations/0029_topologysnapshot.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-09 17:12
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0028_coverage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TopologySnapshot',
|
||||
fields=[
|
||||
('topology_snapshot_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('topology_id', models.IntegerField()),
|
||||
('trace_session_id', models.IntegerField()),
|
||||
('snapshot_data', models.TextField(verbose_name=b'TopologySnapshot')),
|
||||
('order', models.IntegerField()),
|
||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')),
|
||||
],
|
||||
),
|
||||
]
|
||||
66
awx/network_ui/migrations/0030_auto_20180110_1751.py
Normal file
66
awx/network_ui/migrations/0030_auto_20180110_1751.py
Normal file
@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-10 17:51
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0029_topologysnapshot'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CodeUnderTest',
|
||||
fields=[
|
||||
('code_under_test_id', models.AutoField(primary_key=True, serialize=False, verbose_name=b'CodeUnderTest')),
|
||||
('version_x', models.IntegerField()),
|
||||
('version_y', models.IntegerField()),
|
||||
('version_z', models.IntegerField()),
|
||||
('commits_since', models.IntegerField()),
|
||||
('commit_hash', models.CharField(max_length=40)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Result',
|
||||
fields=[
|
||||
('result_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=20)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TestCase',
|
||||
fields=[
|
||||
('test_case_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=200, verbose_name=b'TestCase')),
|
||||
('test_case_data', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TestResult',
|
||||
fields=[
|
||||
('test_result_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('time', models.DateTimeField()),
|
||||
('code_under_test', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.CodeUnderTest')),
|
||||
('result', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Result')),
|
||||
('test_case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.TestCase')),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='coverage',
|
||||
name='client',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='coverage',
|
||||
name='trace_session_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='coverage',
|
||||
name='test_result',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='network_ui.TestResult'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
32
awx/network_ui/migrations/0031_auto_20180110_1752.py
Normal file
32
awx/network_ui/migrations/0031_auto_20180110_1752.py
Normal file
@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-10 17:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
results = ['passed',
|
||||
'failed',
|
||||
'errored',
|
||||
'skipped',
|
||||
'aborted',
|
||||
'not run',
|
||||
'blocked']
|
||||
|
||||
def populate_result_types(apps, schema_editor):
|
||||
|
||||
Result = apps.get_model('network_ui', 'Result')
|
||||
for result in results:
|
||||
Result.objects.get_or_create(name=result)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0030_auto_20180110_1751'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=populate_result_types,
|
||||
),
|
||||
]
|
||||
27
awx/network_ui/migrations/0032_auto_20180112_2135.py
Normal file
27
awx/network_ui/migrations/0032_auto_20180112_2135.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-12 21:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0031_auto_20180110_1752'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='testresult',
|
||||
name='client',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='testresult',
|
||||
name='id',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
]
|
||||
82
awx/network_ui/migrations/0033_auto_20180112_2202.py
Normal file
82
awx/network_ui/migrations/0033_auto_20180112_2202.py
Normal file
@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-12 22:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
import yaml
|
||||
|
||||
messages = yaml.load('''
|
||||
messages:
|
||||
- {msg_type: DeviceMove, fields: [msg_type, sender, id, x, y, previous_x, previous_y]}
|
||||
- {msg_type: DeviceCreate, fields: [msg_type, sender, id, x, y, name, type, host_id]}
|
||||
- {msg_type: DeviceDestroy, fields: [msg_type, sender, id, previous_x, previous_y, previous_name, previous_type, previous_host_id]}
|
||||
- {msg_type: DeviceLabelEdit, fields: [msg_type, sender, id, name, previous_name]}
|
||||
- {msg_type: DeviceSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: DeviceUnSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: InterfaceCreate, fields: [msg_type, sender, device_id, id, name]}
|
||||
- {msg_type: InterfaceLabelEdit, fields: [msg_type, sender, id, device_id, name, previous_name]}
|
||||
- {msg_type: LinkLabelEdit, fields: [msg_type, sender, id, name, previous_name]}
|
||||
- {msg_type: LinkCreate, fields: [msg_type, id, sender, name, from_device_id, to_device_id, from_interface_id, to_interface_id]}
|
||||
- {msg_type: LinkDestroy, fields: [msg_type, id, sender, name, from_device_id, to_device_id, from_interface_id, to_interface_id]}
|
||||
- {msg_type: LinkSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: LinkUnSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: Undo, fields: [msg_type, sender, original_message]}
|
||||
- {msg_type: Redo, fields: [msg_type, sender, original_message]}
|
||||
- {msg_type: Deploy, fields: [msg_type, sender]}
|
||||
- {msg_type: Destroy, fields: [msg_type, sender]}
|
||||
- {msg_type: Discover, fields: [msg_type, sender]}
|
||||
- {msg_type: Layout, fields: [msg_type, sender]}
|
||||
- {msg_type: MultipleMessage, fields: [msg_type, sender, messages]}
|
||||
- {msg_type: MouseEvent, fields: [msg_type, sender, x, y, type, trace_id]}
|
||||
- {msg_type: MouseWheelEvent, fields: [msg_type, sender, delta, deltaX, deltaY, type, originalEvent, trace_id]}
|
||||
- {msg_type: KeyEvent, fields: [msg_type, sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey, trace_id]}
|
||||
- {msg_type: TouchEvent, fields: [msg_type, sender, type, touches]}
|
||||
- {msg_type: StartRecording, fields: [msg_type, sender, trace_id]}
|
||||
- {msg_type: StopRecording, fields: [msg_type, sender, trace_id]}
|
||||
- {msg_type: ViewPort, fields: [msg_type, sender, scale, panX, panY, trace_id]}
|
||||
- {msg_type: CopySite, fields: [msg_type, site]}
|
||||
- {msg_type: GroupMove, fields: [msg_type, sender, id, x1, y1, x2, y2, previous_x1, previous_y1, previous_x2, previous_y2]}
|
||||
- {msg_type: GroupCreate, fields: [msg_type, sender, id, x1, y1, x2, y2, name, type]}
|
||||
- {msg_type: GroupDestroy, fields: [msg_type, sender, id, previous_x1, previous_y1, previous_x2, previous_y2, previous_name, previous_type]}
|
||||
- {msg_type: GroupLabelEdit, fields: [msg_type, sender, id, name, previous_name]}
|
||||
- {msg_type: GroupSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: GroupUnSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: GroupMembership, fields: [msg_type, sender, id, members]}
|
||||
- {msg_type: TableCellEdit, fields: [msg_type, sender, sheet, col, row, old_value, new_value]}
|
||||
- {msg_type: ProcessCreate, fields: [msg_type, id, name, type, device_id, x, y]}
|
||||
- {msg_type: StreamCreate, fields: [msg_type, sender, id, from_id, to_id, label]}
|
||||
- {msg_type: StreamDestroy, fields: [msg_type, sender, id, from_id, to_id, label]}
|
||||
- {msg_type: StreamLabelEdit, fields: [msg_type, sender, id, label, previous_label]}
|
||||
- {msg_type: StreamSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: StreamUnSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: FSMTrace, fields: [msg_type, order, sender, trace_id, fsm_name, from_state, to_state, recv_message_type]}
|
||||
- {msg_type: ChannelTrace, fields: [msg_type, sender, trace_id, from_fsm, to_fsm, sent_message_type]}
|
||||
- {msg_type: Snapshot, fields: [msg_type, sender, devices, links, groups, streams, order, trace_id]}
|
||||
- {msg_type: EnableTest, fields: [msg_type]}
|
||||
- {msg_type: DisableTest, fields: [msg_type]}
|
||||
- {msg_type: StartTest, fields: [msg_type]}
|
||||
- {msg_type: TestCompleted, fields: [msg_type]}
|
||||
- {msg_type: TestResult, fields: [msg_type, sender, id, name, result, date, code_under_test]}
|
||||
- {msg_type: Coverage, fields: [msg_type, sender, coverage, result_id]}
|
||||
''')
|
||||
|
||||
|
||||
def populate_message_types(apps, schema_editor):
|
||||
|
||||
MessageType = apps.get_model('network_ui', 'MessageType')
|
||||
for message in messages['messages']:
|
||||
MessageType.objects.get_or_create(name=message['msg_type'])
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0032_auto_20180112_2135'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=populate_message_types,
|
||||
),
|
||||
]
|
||||
24
awx/network_ui/migrations/0034_auto_20180113_1725.py
Normal file
24
awx/network_ui/migrations/0034_auto_20180113_1725.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2018-01-13 17:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import json
|
||||
|
||||
def add_load_test_case(apps, schema_editor):
|
||||
|
||||
TestCase = apps.get_model('network_ui', 'TestCase')
|
||||
TestCase.objects.get_or_create(name="Load", test_case_data=json.dumps(dict(runnable=False)))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('network_ui', '0033_auto_20180112_2202'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=add_load_test_case,
|
||||
),
|
||||
]
|
||||
@ -175,3 +175,63 @@ class TopologyInventory(models.Model):
|
||||
topology_inventory_id = models.AutoField(primary_key=True,)
|
||||
topology = models.ForeignKey('Topology',)
|
||||
inventory_id = models.IntegerField()
|
||||
|
||||
|
||||
class EventTrace(models.Model):
|
||||
|
||||
event_trace_id = models.AutoField(primary_key=True,)
|
||||
client = models.ForeignKey('Client',)
|
||||
trace_session_id = models.IntegerField(default=0)
|
||||
event_data = models.TextField()
|
||||
message_id = models.IntegerField()
|
||||
|
||||
|
||||
class Coverage(models.Model):
|
||||
|
||||
coverage_id = models.AutoField(primary_key=True,)
|
||||
coverage_data = models.TextField()
|
||||
test_result = models.ForeignKey('TestResult',)
|
||||
|
||||
|
||||
class TopologySnapshot(models.Model):
|
||||
|
||||
topology_snapshot_id = models.AutoField(primary_key=True,)
|
||||
client = models.ForeignKey('Client',)
|
||||
topology_id = models.IntegerField()
|
||||
trace_session_id = models.IntegerField()
|
||||
snapshot_data = models.TextField('TopologySnapshot',)
|
||||
order = models.IntegerField()
|
||||
|
||||
|
||||
class TestCase(models.Model):
|
||||
|
||||
test_case_id = models.AutoField(primary_key=True,)
|
||||
name = models.CharField('TestCase', max_length=200,)
|
||||
test_case_data = models.TextField()
|
||||
|
||||
|
||||
class Result(models.Model):
|
||||
|
||||
result_id = models.AutoField(primary_key=True,)
|
||||
name = models.CharField(max_length=20,)
|
||||
|
||||
|
||||
class CodeUnderTest(models.Model):
|
||||
|
||||
code_under_test_id = models.AutoField('CodeUnderTest', primary_key=True,)
|
||||
version_x = models.IntegerField()
|
||||
version_y = models.IntegerField()
|
||||
version_z = models.IntegerField()
|
||||
commits_since = models.IntegerField()
|
||||
commit_hash = models.CharField(max_length=40,)
|
||||
|
||||
|
||||
class TestResult(models.Model):
|
||||
|
||||
test_result_id = models.AutoField(primary_key=True,)
|
||||
test_case = models.ForeignKey('TestCase',)
|
||||
result = models.ForeignKey('Result',)
|
||||
code_under_test = models.ForeignKey('CodeUnderTest',)
|
||||
time = models.DateTimeField()
|
||||
id = models.IntegerField(default=0)
|
||||
client = models.ForeignKey('Client',)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
messages:
|
||||
- {msg_type: DeviceMove, fields: [msg_type, sender, id, x, y, previous_x, previous_y]}
|
||||
- {msg_type: DeviceCreate, fields: [msg_type, sender, id, x, y, name, type]}
|
||||
- {msg_type: DeviceDestroy, fields: [msg_type, sender, id, previous_x, previous_y, previous_name, previous_type]}
|
||||
- {msg_type: DeviceCreate, fields: [msg_type, sender, id, x, y, name, type, host_id]}
|
||||
- {msg_type: DeviceDestroy, fields: [msg_type, sender, id, previous_x, previous_y, previous_name, previous_type, previous_host_id]}
|
||||
- {msg_type: DeviceLabelEdit, fields: [msg_type, sender, id, name, previous_name]}
|
||||
- {msg_type: DeviceSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: DeviceUnSelected, fields: [msg_type, sender, id]}
|
||||
@ -19,14 +19,13 @@ messages:
|
||||
- {msg_type: Discover, fields: [msg_type, sender]}
|
||||
- {msg_type: Layout, fields: [msg_type, sender]}
|
||||
- {msg_type: MultipleMessage, fields: [msg_type, sender, messages]}
|
||||
- {msg_type: Coverage, fields: [msg_type, sender, coverage]}
|
||||
- {msg_type: MouseEvent, fields: [msg_type, sender, x, y, type]}
|
||||
- {msg_type: MouseWheelEvent, fields: [msg_type, sender, delta, deltaX, deltaY, type, originalEvent]}
|
||||
- {msg_type: KeyEvent, fields: [msg_type, sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey]}
|
||||
- {msg_type: MouseEvent, fields: [msg_type, sender, x, y, type, trace_id]}
|
||||
- {msg_type: MouseWheelEvent, fields: [msg_type, sender, delta, deltaX, deltaY, type, originalEvent, trace_id]}
|
||||
- {msg_type: KeyEvent, fields: [msg_type, sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey, trace_id]}
|
||||
- {msg_type: TouchEvent, fields: [msg_type, sender, type, touches]}
|
||||
- {msg_type: StartRecording, fields: [msg_type, sender]}
|
||||
- {msg_type: StopRecording, fields: [msg_type, sender]}
|
||||
- {msg_type: ViewPort, fields: [msg_type, sender, scale, panX, panY]}
|
||||
- {msg_type: StartRecording, fields: [msg_type, sender, trace_id]}
|
||||
- {msg_type: StopRecording, fields: [msg_type, sender, trace_id]}
|
||||
- {msg_type: ViewPort, fields: [msg_type, sender, scale, panX, panY, trace_id]}
|
||||
- {msg_type: CopySite, fields: [msg_type, site]}
|
||||
- {msg_type: GroupMove, fields: [msg_type, sender, id, x1, y1, x2, y2, previous_x1, previous_y1, previous_x2, previous_y2]}
|
||||
- {msg_type: GroupCreate, fields: [msg_type, sender, id, x1, y1, x2, y2, name, type]}
|
||||
@ -44,4 +43,11 @@ messages:
|
||||
- {msg_type: StreamUnSelected, fields: [msg_type, sender, id]}
|
||||
- {msg_type: FSMTrace, fields: [msg_type, order, sender, trace_id, fsm_name, from_state, to_state, recv_message_type]}
|
||||
- {msg_type: ChannelTrace, fields: [msg_type, sender, trace_id, from_fsm, to_fsm, sent_message_type]}
|
||||
- {msg_type: Snapshot, fields: [msg_type, sender, devices, links, groups, streams, order, trace_id]}
|
||||
- {msg_type: EnableTest, fields: [msg_type]}
|
||||
- {msg_type: DisableTest, fields: [msg_type]}
|
||||
- {msg_type: StartTest, fields: [msg_type]}
|
||||
- {msg_type: TestCompleted, fields: [msg_type]}
|
||||
- {msg_type: TestResult, fields: [msg_type, sender, id, name, result, date, code_under_test]}
|
||||
- {msg_type: Coverage, fields: [msg_type, sender, coverage, result_id]}
|
||||
|
||||
|
||||
49
awx/network_ui/static/network_ui/designs/test.yml
Normal file
49
awx/network_ui/static/network_ui/designs/test.yml
Normal file
@ -0,0 +1,49 @@
|
||||
diagram_id: 42
|
||||
name: diagram
|
||||
states:
|
||||
- id: 6
|
||||
label: Reporting
|
||||
x: 926
|
||||
y: 721
|
||||
- id: 2
|
||||
label: Disabled
|
||||
x: 895
|
||||
y: 344
|
||||
- id: 5
|
||||
label: Running
|
||||
x: 720
|
||||
y: 922
|
||||
- id: 1
|
||||
label: Ready
|
||||
x: 722
|
||||
y: 509
|
||||
- id: 3
|
||||
label: Start
|
||||
x: 702
|
||||
y: 186
|
||||
- id: 4
|
||||
label: Loading
|
||||
x: 524
|
||||
y: 710
|
||||
transitions:
|
||||
- from_state: Running
|
||||
label: onTestCompleted
|
||||
to_state: Reporting
|
||||
- from_state: Ready
|
||||
label: onDisable
|
||||
to_state: Disabled
|
||||
- from_state: Start
|
||||
label: start
|
||||
to_state: Disabled
|
||||
- from_state: Loading
|
||||
label: onTestLoaded
|
||||
to_state: Running
|
||||
- from_state: Disabled
|
||||
label: onEnable
|
||||
to_state: Ready
|
||||
- from_state: Reporting
|
||||
label: onTestReported
|
||||
to_state: Ready
|
||||
- from_state: Ready
|
||||
label: onStartTest
|
||||
to_state: Loading
|
||||
112
awx/network_ui/static/network_ui/test.js
Normal file
112
awx/network_ui/static/network_ui/test.js
Normal file
@ -0,0 +1,112 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
|
||||
function _Disabled () {
|
||||
this.name = 'Disabled';
|
||||
}
|
||||
inherits(_Disabled, _State);
|
||||
var Disabled = new _Disabled();
|
||||
exports.Disabled = Disabled;
|
||||
|
||||
function _Start () {
|
||||
this.name = 'Start';
|
||||
}
|
||||
inherits(_Start, _State);
|
||||
var Start = new _Start();
|
||||
exports.Start = Start;
|
||||
|
||||
function _Running () {
|
||||
this.name = 'Running';
|
||||
}
|
||||
inherits(_Running, _State);
|
||||
var Running = new _Running();
|
||||
exports.Running = Running;
|
||||
|
||||
function _Loading () {
|
||||
this.name = 'Loading';
|
||||
}
|
||||
inherits(_Loading, _State);
|
||||
var Loading = new _Loading();
|
||||
exports.Loading = Loading;
|
||||
|
||||
function _Ready () {
|
||||
this.name = 'Ready';
|
||||
}
|
||||
inherits(_Ready, _State);
|
||||
var Ready = new _Ready();
|
||||
exports.Ready = Ready;
|
||||
|
||||
function _Reporting () {
|
||||
this.name = 'Reporting';
|
||||
}
|
||||
inherits(_Reporting, _State);
|
||||
var Reporting = new _Reporting();
|
||||
exports.Reporting = Reporting;
|
||||
|
||||
|
||||
|
||||
|
||||
_Disabled.prototype.onEnable = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Disabled.prototype.onEnable.transitions = ['Ready'];
|
||||
|
||||
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Disabled);
|
||||
|
||||
};
|
||||
_Start.prototype.start.transitions = ['Disabled'];
|
||||
|
||||
|
||||
|
||||
_Running.prototype.onTestCompleted = function (controller) {
|
||||
|
||||
controller.changeState(Reporting);
|
||||
|
||||
};
|
||||
_Running.prototype.onTestCompleted.transitions = ['Reporting'];
|
||||
|
||||
|
||||
|
||||
_Loading.prototype.onTestLoaded = function (controller) {
|
||||
|
||||
controller.changeState(Running);
|
||||
|
||||
};
|
||||
_Loading.prototype.onTestLoaded.transitions = ['Running'];
|
||||
|
||||
|
||||
|
||||
_Ready.prototype.onDisable = function (controller) {
|
||||
|
||||
controller.changeState(Disabled);
|
||||
|
||||
};
|
||||
_Ready.prototype.onDisable.transitions = ['Disabled'];
|
||||
|
||||
_Ready.prototype.onStartTest = function (controller) {
|
||||
|
||||
controller.changeState(Loading);
|
||||
|
||||
};
|
||||
_Ready.prototype.onStartTest.transitions = ['Loading'];
|
||||
|
||||
|
||||
|
||||
_Reporting.prototype.onTestReported = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
|
||||
};
|
||||
_Reporting.prototype.onTestReported.transitions = ['Ready'];
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
<ul>
|
||||
<li><a href="/#/topology/">New</a></li>
|
||||
{%for o in topologies%}
|
||||
<li>
|
||||
<a href="/#/topology?topology_id={{o.pk}}">{{o.pk}} {{o}}</a>
|
||||
{%for device in o.device_set.all%}
|
||||
{{device}}
|
||||
{%endfor%}
|
||||
</li>
|
||||
{%endfor%}
|
||||
</ul>
|
||||
5
awx/network_ui/templates/network_ui/upload_test.html
Normal file
5
awx/network_ui/templates/network_ui/upload_test.html
Normal file
@ -0,0 +1,5 @@
|
||||
<form action="/network_ui/upload_test" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{form}}
|
||||
<button type="submit">Upload Test</button>
|
||||
</form>
|
||||
13
awx/network_ui/tools/Makefile
Normal file
13
awx/network_ui/tools/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
SERVER = "https://meganuke:8043"
|
||||
PORT = "9000"
|
||||
|
||||
.PHONY: clean coverage
|
||||
|
||||
clean:
|
||||
git clean -fdX .
|
||||
git clean -fd .
|
||||
|
||||
coverage:
|
||||
./coverage_report.py ${SERVER}
|
||||
python -m SimpleHTTPServer ${PORT}
|
||||
61
awx/network_ui/tools/coverage_report.py
Executable file
61
awx/network_ui/tools/coverage_report.py
Executable file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Usage:
|
||||
coverage_report [options] <server>
|
||||
|
||||
Options:
|
||||
-h, --help Show this page
|
||||
--debug Show debug logging
|
||||
--verbose Show verbose logging
|
||||
"""
|
||||
from docopt import docopt
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import requests
|
||||
import subprocess
|
||||
|
||||
logger = logging.getLogger('coverage_report')
|
||||
|
||||
TESTS_API = '/network_ui/tests'
|
||||
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
print (parsed_args['<server>'])
|
||||
server = parsed_args['<server>']
|
||||
|
||||
tests = requests.get(server + TESTS_API, verify=False).json()
|
||||
|
||||
for test in tests['tests']:
|
||||
if not os.path.exists(test['name']):
|
||||
os.mkdir(test['name'])
|
||||
with open(test['name'] + "/coverage.json", 'w') as f:
|
||||
f.write(requests.get(server + test['coverage'], verify=False).text)
|
||||
|
||||
|
||||
for test in tests['tests']:
|
||||
subprocess.Popen('istanbul report html', shell=True, cwd=test['name']).wait()
|
||||
|
||||
|
||||
subprocess.Popen('istanbul report html', shell=True).wait()
|
||||
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
2
awx/network_ui/tools/requirements.txt
Normal file
2
awx/network_ui/tools/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
requests
|
||||
docopt
|
||||
@ -7,7 +7,11 @@ import awx.network_ui.routing
|
||||
|
||||
app_name = 'network_ui'
|
||||
urlpatterns = [
|
||||
url(r'^tests$', views.tests, name='tests'),
|
||||
url(r'^upload_test$', views.upload_test, name='upload_test'),
|
||||
url(r'^download_coverage/(?P<pk>[0-9]+)$', views.download_coverage, name='download_coverage'),
|
||||
url(r'^download_trace$', views.download_trace, name='download_trace'),
|
||||
url(r'^download_recording$', views.download_recording, name='download_recording'),
|
||||
url(r'^topology.json$', views.json_topology_data, name='json_topology_data'),
|
||||
url(r'^topology.yaml$', views.yaml_topology_data, name='json_topology_data'),
|
||||
url(r'^$', views.index, name='index'),
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
# Copyright (c) 2017 Red Hat, Inc
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from django.http import JsonResponse, HttpResponseBadRequest, HttpResponse
|
||||
from django.http import JsonResponse, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
import yaml
|
||||
|
||||
import json
|
||||
|
||||
|
||||
# Create your views here.
|
||||
from .models import Topology, FSMTrace
|
||||
from .models import Topology, FSMTrace, EventTrace, TopologySnapshot
|
||||
from .models import TestCase, TestResult, Coverage
|
||||
from .serializers import topology_data
|
||||
|
||||
|
||||
@ -56,3 +60,79 @@ def download_trace(request):
|
||||
return response
|
||||
else:
|
||||
return HttpResponse(form.errors)
|
||||
|
||||
|
||||
class RecordingForm(forms.Form):
|
||||
topology_id = forms.IntegerField()
|
||||
trace_id = forms.IntegerField()
|
||||
client_id = forms.IntegerField()
|
||||
|
||||
|
||||
def download_recording(request):
|
||||
form = RecordingForm(request.GET)
|
||||
if form.is_valid():
|
||||
topology_id = form.cleaned_data['topology_id']
|
||||
trace_id = form.cleaned_data['trace_id']
|
||||
client_id = form.cleaned_data['client_id']
|
||||
data = dict()
|
||||
data['event_trace'] = [json.loads(x) for x in EventTrace
|
||||
.objects.filter(trace_session_id=trace_id, client_id=client_id)
|
||||
.order_by('message_id')
|
||||
.values_list('event_data', flat=True)]
|
||||
data['fsm_trace'] = list(FSMTrace
|
||||
.objects
|
||||
.filter(trace_session_id=trace_id, client_id=client_id)
|
||||
.order_by('order')
|
||||
.values())
|
||||
data['snapshots'] = [json.loads(x) for x in TopologySnapshot
|
||||
.objects.filter(trace_session_id=trace_id, client_id=client_id)
|
||||
.order_by('order')
|
||||
.values_list('snapshot_data', flat=True)]
|
||||
response = HttpResponse(json.dumps(data, sort_keys=True, indent=4),
|
||||
content_type="application/force-download")
|
||||
response['Content-Disposition'] = 'attachment; filename="trace_{0}_{1}_{2}.yml"'.format(topology_id, client_id, trace_id)
|
||||
return response
|
||||
else:
|
||||
return HttpResponse(form.errors)
|
||||
|
||||
|
||||
def tests(request):
|
||||
tests = list(TestCase.objects.all().values('test_case_id', 'name'))
|
||||
for x in tests:
|
||||
x['coverage'] = "/network_ui/download_coverage/{0}".format(x['test_case_id'])
|
||||
return JsonResponse(dict(tests=tests))
|
||||
|
||||
|
||||
def create_test(name, data):
|
||||
try:
|
||||
test_case = TestCase.objects.get(name=name)
|
||||
test_case.test_case_data=json.dumps(data)
|
||||
test_case.save()
|
||||
except ObjectDoesNotExist:
|
||||
TestCase(name=name, test_case_data=json.dumps(data)).save()
|
||||
|
||||
|
||||
class UploadTestForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
file = forms.FileField()
|
||||
|
||||
|
||||
def upload_test(request):
|
||||
if request.method == 'POST':
|
||||
form = UploadTestForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
name = form.cleaned_data['name']
|
||||
data = json.loads(request.FILES['file'].read())
|
||||
create_test(name, data)
|
||||
return HttpResponseRedirect('/network_ui/tests')
|
||||
else:
|
||||
form = UploadTestForm()
|
||||
return render(request, 'network_ui/upload_test.html', {'form': form})
|
||||
|
||||
|
||||
def download_coverage(request, pk):
|
||||
latest_tr = TestResult.objects.filter(test_case_id=pk).order_by('-time')[0]
|
||||
coverage = Coverage.objects.get(test_result_id=latest_tr.pk)
|
||||
response = HttpResponse(coverage.coverage_data,
|
||||
content_type="application/json")
|
||||
return response
|
||||
|
||||
@ -58,6 +58,17 @@ const base = {
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: 'istanbul-instrumenter-loader',
|
||||
options: { esModules: true }
|
||||
},
|
||||
enforce: 'pre',
|
||||
include: [
|
||||
/src\/network-ui\//
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
|
||||
@ -176,23 +176,18 @@ function MultipleMessage(sender, messages) {
|
||||
}
|
||||
exports.MultipleMessage = MultipleMessage;
|
||||
|
||||
function Coverage(sender, coverage) {
|
||||
this.msg_type = "Coverage";
|
||||
this.sender = sender;
|
||||
this.coverage = coverage;
|
||||
}
|
||||
exports.Coverage = Coverage;
|
||||
|
||||
function MouseEvent(sender, x, y, type) {
|
||||
function MouseEvent(sender, x, y, type, trace_id) {
|
||||
this.msg_type = "MouseEvent";
|
||||
this.sender = sender;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.type = type;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.MouseEvent = MouseEvent;
|
||||
|
||||
function MouseWheelEvent(sender, delta, deltaX, deltaY, type, metaKey) {
|
||||
function MouseWheelEvent(sender, delta, deltaX, deltaY, type, metaKey, trace_id) {
|
||||
this.msg_type = "MouseWheelEvent";
|
||||
this.sender = sender;
|
||||
this.delta = delta;
|
||||
@ -200,10 +195,11 @@ function MouseWheelEvent(sender, delta, deltaX, deltaY, type, metaKey) {
|
||||
this.deltaY = deltaY;
|
||||
this.type = type;
|
||||
this.originalEvent = {metaKey: metaKey};
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.MouseWheelEvent = MouseWheelEvent;
|
||||
|
||||
function KeyEvent(sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey) {
|
||||
function KeyEvent(sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey, trace_id) {
|
||||
this.msg_type = "KeyEvent";
|
||||
this.sender = sender;
|
||||
this.key = key;
|
||||
@ -213,6 +209,7 @@ function KeyEvent(sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey
|
||||
this.shiftKey = shiftKey;
|
||||
this.ctrlKey = ctrlKey;
|
||||
this.metaKey = metaKey;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.KeyEvent = KeyEvent;
|
||||
|
||||
@ -224,24 +221,27 @@ function TouchEvent(sender, type, touches) {
|
||||
}
|
||||
exports.TouchEvent = TouchEvent;
|
||||
|
||||
function StartRecording(sender) {
|
||||
function StartRecording(sender, trace_id) {
|
||||
this.msg_type = "StartRecording";
|
||||
this.sender = sender;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.StartRecording = StartRecording;
|
||||
|
||||
function StopRecording(sender) {
|
||||
function StopRecording(sender, trace_id) {
|
||||
this.msg_type = "StopRecording";
|
||||
this.sender = sender;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.StopRecording = StopRecording;
|
||||
|
||||
function ViewPort(sender, scale, panX, panY) {
|
||||
function ViewPort(sender, scale, panX, panY, trace_id) {
|
||||
this.msg_type = "ViewPort";
|
||||
this.sender = sender;
|
||||
this.scale = scale;
|
||||
this.panX = panX;
|
||||
this.panY = panY;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.ViewPort = ViewPort;
|
||||
|
||||
@ -446,3 +446,54 @@ function ChannelTrace(from_fsm, to_fsm, sent_message_type) {
|
||||
this.sent_message_type = sent_message_type;
|
||||
}
|
||||
exports.ChannelTrace = ChannelTrace;
|
||||
|
||||
function Snapshot(sender, devices, links, groups, streams, order, trace_id) {
|
||||
this.msg_type = 'Snapshot';
|
||||
this.sender = 0;
|
||||
this.devices = devices;
|
||||
this.links = links;
|
||||
this.groups = groups;
|
||||
this.streams = streams;
|
||||
this.order = order;
|
||||
this.trace_id = trace_id;
|
||||
}
|
||||
exports.Snapshot = Snapshot;
|
||||
|
||||
function EnableTest() {
|
||||
this.msg_type = "EnableTest";
|
||||
}
|
||||
exports.EnableTest = EnableTest;
|
||||
|
||||
function DisableTest() {
|
||||
this.msg_type = "DisableTest";
|
||||
}
|
||||
exports.DisableTest = DisableTest;
|
||||
|
||||
function StartTest() {
|
||||
this.msg_type = "StartTest";
|
||||
}
|
||||
exports.StartTest = StartTest;
|
||||
|
||||
function TestCompleted() {
|
||||
this.msg_type = "TestCompleted";
|
||||
}
|
||||
exports.TestCompleted = TestCompleted;
|
||||
|
||||
function TestResult(sender, id, name, result, date, code_under_test) {
|
||||
this.msg_type = "TestResult";
|
||||
this.sender = sender;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.result = result;
|
||||
this.date = date;
|
||||
this.code_under_test = code_under_test;
|
||||
}
|
||||
exports.TestResult = TestResult;
|
||||
|
||||
function Coverage(sender, coverage, result_id) {
|
||||
this.msg_type = "Coverage";
|
||||
this.sender = sender;
|
||||
this.coverage = coverage;
|
||||
this.result_id = result_id;
|
||||
}
|
||||
exports.Coverage = Coverage;
|
||||
|
||||
@ -36,8 +36,12 @@ Device.prototype.toJSON = function () {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
type: this.type,
|
||||
interfaces: this.interfaces,
|
||||
processes: this.processes};
|
||||
interfaces: this.interfaces.map(function (x) {
|
||||
return x.toJSON();
|
||||
}),
|
||||
processes: this.processes.map(function (x) {
|
||||
return x.toJSON();
|
||||
})};
|
||||
};
|
||||
|
||||
Device.prototype.is_selected = function (x, y) {
|
||||
@ -726,6 +730,11 @@ function Process(id, name, type, x, y) {
|
||||
}
|
||||
exports.Process = Process;
|
||||
|
||||
Process.prototype.toJSON = function () {
|
||||
return {id: this.id,
|
||||
name: this.name};
|
||||
};
|
||||
|
||||
function Stream(id, from_device, to_device, label) {
|
||||
this.id = id;
|
||||
this.from_device = from_device;
|
||||
@ -919,3 +928,21 @@ Stream.prototype.start_arc_angle_rad = function () {
|
||||
Stream.prototype.start_arc_angle = function () {
|
||||
return this.start_arc_angle_rad() * 180 / Math.PI;
|
||||
};
|
||||
|
||||
function Test(name, event_trace, fsm_trace, pre_test_snapshot, post_test_snapshot) {
|
||||
this.name = name;
|
||||
this.event_trace = event_trace;
|
||||
this.fsm_trace = fsm_trace;
|
||||
this.pre_test_snapshot = pre_test_snapshot;
|
||||
this.post_test_snapshot = post_test_snapshot;
|
||||
}
|
||||
exports.Test = Test;
|
||||
|
||||
function TestResult(id, name, result, date, code_under_test) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.result = result;
|
||||
this.date = date;
|
||||
this.code_under_test = code_under_test;
|
||||
}
|
||||
exports.TestResult = TestResult;
|
||||
|
||||
@ -33,6 +33,7 @@ var inventoryToolboxClipPath = require('./inventory.toolbox.clip.path.directive.
|
||||
var statusLight = require('./status.light.directive.js');
|
||||
var taskStatus = require('./task.status.directive.js');
|
||||
var debug = require('./debug.directive.js');
|
||||
var test_results = require('./test_results.directive.js');
|
||||
var awxNetworkUI = require('./network.ui.directive.js');
|
||||
|
||||
var networkUI = angular.module('networkUI', [
|
||||
@ -70,6 +71,7 @@ var networkUI = angular.module('networkUI', [
|
||||
.directive('awxNetInventoryToolboxClipPath', inventoryToolboxClipPath.inventoryToolboxClipPath)
|
||||
.directive('awxNetStatusLight', statusLight.statusLight)
|
||||
.directive('awxNetTaskStatus', taskStatus.taskStatus)
|
||||
.directive('awxNetTestResults', test_results.test_results)
|
||||
.directive('awxNetworkUi', awxNetworkUI.awxNetworkUI);
|
||||
|
||||
exports.networkUI = networkUI;
|
||||
|
||||
@ -15,6 +15,7 @@ var stream_fsm = require('./stream.fsm.js');
|
||||
var group = require('./group.js');
|
||||
var buttons = require('./buttons.js');
|
||||
var time = require('./time.js');
|
||||
var test_fsm = require('./test.fsm.js');
|
||||
var util = require('./util.js');
|
||||
var models = require('./models.js');
|
||||
var messages = require('./messages.js');
|
||||
@ -22,7 +23,7 @@ var svg_crowbar = require('./svg-crowbar.js');
|
||||
var ReconnectingWebSocket = require('reconnectingwebsocket');
|
||||
|
||||
var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$q, $state, ProcessErrors) {
|
||||
$q, $state, ProcessErrors, ConfigService) {
|
||||
|
||||
window.scope = $scope;
|
||||
var i = 0;
|
||||
@ -96,6 +97,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.group_id_seq = util.natural_numbers(0);
|
||||
$scope.message_id_seq = util.natural_numbers(0);
|
||||
$scope.stream_id_seq = util.natural_numbers(0);
|
||||
$scope.test_result_id_seq = util.natural_numbers(0);
|
||||
$scope.overall_toolbox_collapsed = false;
|
||||
$scope.time_pointer = -1;
|
||||
$scope.frame = 0;
|
||||
@ -108,6 +110,14 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.groups = [];
|
||||
$scope.processes = [];
|
||||
$scope.configurations = [];
|
||||
$scope.tests = [];
|
||||
$scope.current_tests = [];
|
||||
$scope.current_test = null;
|
||||
$scope.testing = false;
|
||||
$scope.version = null;
|
||||
$scope.test_events = [];
|
||||
$scope.test_results = [];
|
||||
$scope.test_errors = [];
|
||||
$scope.streams = [];
|
||||
$scope.view_port = {'x': 0,
|
||||
'y': 0,
|
||||
@ -118,7 +128,9 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.trace_id = $scope.trace_id_seq();
|
||||
|
||||
$scope.send_trace_message = function (message) {
|
||||
console.log(message);
|
||||
if (!$scope.recording) {
|
||||
return;
|
||||
}
|
||||
message.sender = $scope.client_id;
|
||||
message.trace_id = $scope.trace_id;
|
||||
message.message_id = $scope.message_id_seq();
|
||||
@ -148,6 +160,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.site_controller = new fsm.FSMController($scope, "site_fsm", site_fsm.Disable, $scope);
|
||||
$scope.buttons_controller = new fsm.FSMController($scope, "buttons_fsm", buttons.Start, $scope);
|
||||
$scope.time_controller = new fsm.FSMController($scope, "time_fsm", time.Start, $scope);
|
||||
$scope.test_controller = new fsm.FSMController($scope, "test_fsm", test_fsm.Start, $scope);
|
||||
$scope.app_toolbox_controller = new fsm.FSMController($scope, "toolbox_fsm", toolbox_fsm.Start, $scope);
|
||||
|
||||
//App Toolbox Setup
|
||||
@ -185,6 +198,12 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
let host = hosts[i];
|
||||
console.log(host);
|
||||
host.data = jsyaml.safeLoad(host.variables);
|
||||
if (host.data.type == undefined) {
|
||||
host.data.type = 'unknown';
|
||||
}
|
||||
if (host.data.name == undefined) {
|
||||
host.data.name = host.name;
|
||||
}
|
||||
var device = new models.Device(0, host.data.name, 0, 0, host.data.type, host.id, host.variables);
|
||||
device.icon = true;
|
||||
$scope.inventory_toolbox.items.push(device);
|
||||
@ -288,9 +307,13 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.mode_controller.delegate_channel = new fsm.Channel($scope.mode_controller,
|
||||
$scope.time_controller,
|
||||
$scope);
|
||||
$scope.test_controller.delegate_channel = new fsm.Channel($scope.test_controller,
|
||||
$scope.mode_controller,
|
||||
$scope);
|
||||
|
||||
|
||||
$scope.first_channel = new fsm.Channel(null,
|
||||
$scope.mode_controller,
|
||||
$scope.test_controller,
|
||||
$scope);
|
||||
|
||||
var getMouseEventResult = function (mouseEvent) {
|
||||
@ -464,7 +487,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.onMouseDown = function ($event) {
|
||||
$scope.normalize_mouse_event($event);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type));
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type, $scope.trace_id));
|
||||
}
|
||||
$scope.last_event = $event;
|
||||
$scope.first_channel.send('MouseDown', $event);
|
||||
@ -475,7 +498,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.onMouseUp = function ($event) {
|
||||
$scope.normalize_mouse_event($event);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type));
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type, $scope.trace_id));
|
||||
}
|
||||
$scope.last_event = $event;
|
||||
$scope.first_channel.send('MouseUp', $event);
|
||||
@ -486,7 +509,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.onMouseLeave = function ($event) {
|
||||
$scope.normalize_mouse_event($event);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type));
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type, $scope.trace_id));
|
||||
}
|
||||
$scope.onMouseLeaveResult = getMouseEventResult($event);
|
||||
$scope.cursor.hidden = true;
|
||||
@ -496,7 +519,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.onMouseMove = function ($event) {
|
||||
$scope.normalize_mouse_event($event);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type));
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type, $scope.trace_id));
|
||||
}
|
||||
//var coords = getCrossBrowserElementCoords($event);
|
||||
$scope.cursor.hidden = false;
|
||||
@ -513,7 +536,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$scope.onMouseOver = function ($event) {
|
||||
$scope.normalize_mouse_event($event);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type));
|
||||
$scope.send_control_message(new messages.MouseEvent($scope.client_id, $event.x, $event.y, $event.type, $scope.trace_id));
|
||||
}
|
||||
$scope.onMouseOverResult = getMouseEventResult($event);
|
||||
$scope.cursor.hidden = false;
|
||||
@ -529,11 +552,11 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
var deltaY = $event.deltaY;
|
||||
// console.log([$event, delta, deltaX, deltaY]);
|
||||
if ($scope.recording) {
|
||||
$scope.send_control_message(new messages.MouseWheelEvent($scope.client_id, delta, deltaX, deltaY, $event.type, $event.originalEvent.metaKey));
|
||||
$scope.send_control_message(new messages.MouseWheelEvent($scope.client_id, delta, deltaX, deltaY, $event.type, $event.originalEvent.metaKey, $scope.trace_id));
|
||||
}
|
||||
$scope.last_event = $event;
|
||||
$scope.first_channel.send('MouseWheel', [$event, delta, deltaX, deltaY]);
|
||||
event.preventDefault();
|
||||
$event.preventDefault();
|
||||
};
|
||||
|
||||
$scope.onKeyDown = function ($event) {
|
||||
@ -545,7 +568,8 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
$event.altKey,
|
||||
$event.shiftKey,
|
||||
$event.ctrlKey,
|
||||
$event.metaKey));
|
||||
$event.metaKey,
|
||||
$scope.trace_id));
|
||||
}
|
||||
$scope.last_event = $event;
|
||||
$scope.last_key = $event.key;
|
||||
@ -653,6 +677,7 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
};
|
||||
|
||||
$scope.onRenameContextButton = function (button) {
|
||||
console.log(button.name);
|
||||
$scope.context_menus[0].enabled = false;
|
||||
$scope.first_channel.send("LabelEdit", {});
|
||||
};
|
||||
@ -690,14 +715,31 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
console.log(button.name);
|
||||
$scope.recording = ! $scope.recording;
|
||||
if ($scope.recording) {
|
||||
$scope.trace_id = $scope.trace_id_seq();
|
||||
$scope.send_control_message(new messages.MultipleMessage($scope.client_id,
|
||||
[new messages.StartRecording($scope.client_id),
|
||||
[new messages.StartRecording($scope.client_id, $scope.trace_id),
|
||||
new messages.ViewPort($scope.client_id,
|
||||
$scope.current_scale,
|
||||
$scope.panX,
|
||||
$scope.panY)]));
|
||||
$scope.panY,
|
||||
$scope.trace_id),
|
||||
new messages.Snapshot($scope.client_id,
|
||||
$scope.devices,
|
||||
$scope.links,
|
||||
$scope.groups,
|
||||
$scope.streams,
|
||||
0,
|
||||
$scope.trace_id)]));
|
||||
} else {
|
||||
$scope.send_control_message(new messages.StopRecording($scope.client_id));
|
||||
$scope.send_control_message(new messages.MultipleMessage($scope.client_id,
|
||||
[new messages.Snapshot($scope.client_id,
|
||||
$scope.devices,
|
||||
$scope.links,
|
||||
$scope.groups,
|
||||
$scope.streams,
|
||||
1,
|
||||
$scope.trace_id),
|
||||
new messages.StopRecording($scope.client_id, $scope.trace_id)]));
|
||||
}
|
||||
};
|
||||
|
||||
@ -789,6 +831,25 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
console.log(button.label);
|
||||
window.open("/network_ui/download_trace?topology_id=" + $scope.topology_id + "&trace_id=" + $scope.trace_id + "&client_id=" + $scope.client_id);
|
||||
};
|
||||
|
||||
$scope.onDownloadRecordingButton = function (button) {
|
||||
console.log(button.label);
|
||||
window.open("/network_ui/download_recording?topology_id=" + $scope.topology_id + "&trace_id=" + $scope.trace_id + "&client_id=" + $scope.client_id);
|
||||
};
|
||||
|
||||
$scope.onUploadTestButton = function (button) {
|
||||
console.log(button.name);
|
||||
window.open("/network_ui/upload_test", "_top");
|
||||
};
|
||||
|
||||
$scope.onRunTestsButton = function (button) {
|
||||
console.log(button.name);
|
||||
|
||||
$scope.test_results = [];
|
||||
$scope.current_tests = $scope.tests.slice();
|
||||
$scope.first_channel.send("EnableTest", new messages.EnableTest());
|
||||
};
|
||||
|
||||
// Buttons
|
||||
var button_offset = 200;
|
||||
|
||||
@ -802,6 +863,9 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
new models.Button("CONFIGURE", button_offset + 520, 48, 90, 30, $scope.onConfigureButton, $scope),
|
||||
new models.Button("EXPORT YAML", button_offset + 620, 48, 120, 30, $scope.onExportYamlButton, $scope),
|
||||
new models.Button("DOWNLOAD TRACE", button_offset + 750, 48, 150, 30, $scope.onDownloadTraceButton, $scope),
|
||||
new models.Button("DOWNLOAD RECORDING", button_offset + 910, 48, 170, 30, $scope.onDownloadRecordingButton, $scope),
|
||||
new models.Button("UPLOAD TEST", button_offset + 10, 88, 100, 30, $scope.onUploadTestButton, $scope),
|
||||
new models.Button("RUN TESTS", button_offset + 120, 88, 100, 30, $scope.onRunTestsButton, $scope),
|
||||
];
|
||||
|
||||
var LAYERS_X = 160;
|
||||
@ -1429,6 +1493,8 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
//Erase the existing state
|
||||
$scope.devices = [];
|
||||
$scope.links = [];
|
||||
$scope.groups = [];
|
||||
$scope.streams = [];
|
||||
|
||||
var device_map = {};
|
||||
var device_interface_map = {};
|
||||
@ -1634,12 +1700,6 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.send_coverage = function () {
|
||||
console.log("Sending coverage");
|
||||
if (typeof(window.__coverage__) !== "undefined" && window.__coverage__ !== null) {
|
||||
$scope.send_control_message(new messages.Coverage($scope.client_id, window.__coverage__));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.control_socket.onmessage = function(message) {
|
||||
@ -1743,6 +1803,112 @@ var NetworkUIController = function($scope, $document, $location, $window, $http,
|
||||
map.set(key, stream.offset + 1);
|
||||
}
|
||||
};
|
||||
|
||||
setInterval( function () {
|
||||
var test_event = null;
|
||||
if ($scope.test_events.length > 0) {
|
||||
test_event = $scope.test_events.shift();
|
||||
console.log(test_event);
|
||||
test_event.sender = 0;
|
||||
try {
|
||||
$scope.first_channel.send(test_event.msg_type, test_event);
|
||||
} catch (err) {
|
||||
$scope.test_errors.push(err);
|
||||
}
|
||||
}
|
||||
$scope.$apply();
|
||||
}, 10);
|
||||
|
||||
ConfigService
|
||||
.getConfig()
|
||||
.then(function(config){
|
||||
$scope.version = config.version;
|
||||
});
|
||||
|
||||
$scope.reset_coverage = function() {
|
||||
var i = null;
|
||||
var coverage = null;
|
||||
var f = null;
|
||||
if (typeof(window.__coverage__) !== "undefined" && window.__coverage__ !== null) {
|
||||
for (f in window.__coverage__) {
|
||||
coverage = window.__coverage__[f];
|
||||
for (i in coverage.b) {
|
||||
coverage.b[i] = [0, 0];
|
||||
}
|
||||
for (i in coverage.f) {
|
||||
coverage.f[i] = 0;
|
||||
}
|
||||
for (i in coverage.s) {
|
||||
coverage.s[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset_flags = function () {
|
||||
$scope.debug = {'hidden': true};
|
||||
$scope.hide_buttons = false;
|
||||
$scope.hide_links = false;
|
||||
$scope.hide_interfaces = false;
|
||||
$scope.hide_groups = false;
|
||||
};
|
||||
|
||||
|
||||
$scope.reset_fsm_state = function () {
|
||||
$scope.null_controller.state = null_fsm.Start;
|
||||
$scope.null_controller.state.start($scope.null_controller);
|
||||
$scope.hotkeys_controller.state = hotkeys.Start;
|
||||
$scope.hotkeys_controller.state.start($scope.hotkeys_controller);
|
||||
$scope.view_controller.state = view.Start;
|
||||
$scope.view_controller.state.start($scope.view_controller);
|
||||
$scope.device_detail_controller.state = device_detail_fsm.Start;
|
||||
$scope.device_detail_controller.state.start($scope.device_detail_controller);
|
||||
$scope.move_controller.state = move.Start;
|
||||
$scope.move_controller.state.start($scope.move_controller);
|
||||
$scope.link_controller.state = link.Start;
|
||||
$scope.link_controller.state.start($scope.link_controller);
|
||||
$scope.stream_controller.state = stream_fsm.Start;
|
||||
$scope.stream_controller.state.start($scope.stream_controller);
|
||||
$scope.group_controller.state = group.Start;
|
||||
$scope.group_controller.state.start($scope.group_controller);
|
||||
$scope.rack_controller.state = rack_fsm.Disable;
|
||||
$scope.rack_controller.state.start($scope.rack_controller);
|
||||
$scope.site_controller.state = site_fsm.Disable;
|
||||
$scope.site_controller.state.start($scope.site_controller);
|
||||
$scope.buttons_controller.state = buttons.Start;
|
||||
$scope.buttons_controller.state.start($scope.buttons_controller);
|
||||
$scope.time_controller.state = time.Start;
|
||||
$scope.time_controller.state.start($scope.time_controller);
|
||||
$scope.app_toolbox_controller.state = toolbox_fsm.Start;
|
||||
$scope.app_toolbox_controller.state.start($scope.app_toolbox_controller);
|
||||
$scope.inventory_toolbox_controller.state = toolbox_fsm.Start;
|
||||
$scope.inventory_toolbox_controller.state.start($scope.inventory_toolbox_controller);
|
||||
$scope.rack_toolbox_controller.state = toolbox_fsm.Start;
|
||||
$scope.rack_toolbox_controller.state.start($scope.rack_toolbox_controller);
|
||||
$scope.site_toolbox_controller.state = toolbox_fsm.Start;
|
||||
$scope.site_toolbox_controller.state.start($scope.site_toolbox_controller);
|
||||
$scope.mode_controller.state = mode_fsm.Start;
|
||||
$scope.mode_controller.state.start($scope.mode_controller);
|
||||
};
|
||||
|
||||
$scope.reset_history = function () {
|
||||
$scope.history = [];
|
||||
};
|
||||
|
||||
$scope.reset_toolboxes = function () {
|
||||
$scope.app_toolbox.items = [];
|
||||
$scope.app_toolbox.items.push(new models.Process(0, 'BGP', 'process', 0, 0));
|
||||
$scope.app_toolbox.items.push(new models.Process(0, 'OSPF', 'process', 0, 0));
|
||||
$scope.app_toolbox.items.push(new models.Process(0, 'STP', 'process', 0, 0));
|
||||
$scope.app_toolbox.items.push(new models.Process(0, 'Zero Pipeline', 'process', 0, 0));
|
||||
|
||||
for(i = 0; i < $scope.app_toolbox.items.length; i++) {
|
||||
$scope.app_toolbox.items[i].icon = true;
|
||||
}
|
||||
$scope.inventory_toolbox.items = [];
|
||||
$scope.rack_toolbox.items = [];
|
||||
$scope.site_toolbox.items = [];
|
||||
};
|
||||
};
|
||||
|
||||
exports.NetworkUIController = NetworkUIController;
|
||||
|
||||
@ -116,6 +116,7 @@
|
||||
ng-attr-transform="translate({{context_menus[0].x}}, {{context_menus[0].y}})">
|
||||
</g>
|
||||
<g awx-net-debug></g>
|
||||
<g awx-net-test-results></g>
|
||||
<g awx-net-cursor></g>
|
||||
<g ng-repeat="touch in touches">
|
||||
<g awx-net-touch></g>
|
||||
|
||||
@ -766,3 +766,8 @@
|
||||
.NetworkUI__contextMenuButton-pressed{
|
||||
fill:@button-body-hover;
|
||||
}
|
||||
|
||||
.NetworkUI__test_results {
|
||||
fill: @light-background;
|
||||
stroke: @dark-widget-detail;
|
||||
}
|
||||
|
||||
160
awx/ui/client/src/network-ui/test.fsm.js
Normal file
160
awx/ui/client/src/network-ui/test.fsm.js
Normal file
@ -0,0 +1,160 @@
|
||||
var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
var messages = require('./messages.js');
|
||||
var models = require('./models.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
inherits(_State, fsm._State);
|
||||
|
||||
|
||||
function _Disabled () {
|
||||
this.name = 'Disabled';
|
||||
}
|
||||
inherits(_Disabled, _State);
|
||||
var Disabled = new _Disabled();
|
||||
exports.Disabled = Disabled;
|
||||
|
||||
function _Start () {
|
||||
this.name = 'Start';
|
||||
}
|
||||
inherits(_Start, _State);
|
||||
var Start = new _Start();
|
||||
exports.Start = Start;
|
||||
|
||||
function _Running () {
|
||||
this.name = 'Running';
|
||||
}
|
||||
inherits(_Running, _State);
|
||||
var Running = new _Running();
|
||||
exports.Running = Running;
|
||||
|
||||
function _Loading () {
|
||||
this.name = 'Loading';
|
||||
}
|
||||
inherits(_Loading, _State);
|
||||
var Loading = new _Loading();
|
||||
exports.Loading = Loading;
|
||||
|
||||
function _Ready () {
|
||||
this.name = 'Ready';
|
||||
}
|
||||
inherits(_Ready, _State);
|
||||
var Ready = new _Ready();
|
||||
exports.Ready = Ready;
|
||||
|
||||
function _Reporting () {
|
||||
this.name = 'Reporting';
|
||||
}
|
||||
inherits(_Reporting, _State);
|
||||
var Reporting = new _Reporting();
|
||||
exports.Reporting = Reporting;
|
||||
|
||||
|
||||
|
||||
|
||||
_Disabled.prototype.onEnableTest = function (controller) {
|
||||
|
||||
controller.changeState(Ready);
|
||||
};
|
||||
_Disabled.prototype.onEnableTest.transitions = ['Ready'];
|
||||
|
||||
|
||||
|
||||
_Start.prototype.start = function (controller) {
|
||||
|
||||
controller.changeState(Disabled);
|
||||
|
||||
};
|
||||
_Start.prototype.start.transitions = ['Disabled'];
|
||||
|
||||
|
||||
|
||||
_Running.prototype.onTestCompleted = function (controller) {
|
||||
|
||||
controller.changeState(Reporting);
|
||||
};
|
||||
_Running.prototype.onTestCompleted.transitions = ['Reporting'];
|
||||
|
||||
_Reporting.prototype.start = function (controller) {
|
||||
|
||||
var test_result = null;
|
||||
controller.scope.replay = false;
|
||||
controller.scope.disconnected = false;
|
||||
controller.scope.recording = false;
|
||||
var result = "passed";
|
||||
if (controller.scope.test_errors.length > 0) {
|
||||
result = "errored";
|
||||
}
|
||||
test_result = new models.TestResult(controller.scope.test_result_id_seq(),
|
||||
controller.scope.current_test.name,
|
||||
result,
|
||||
new Date().toISOString(),
|
||||
controller.scope.version);
|
||||
controller.scope.test_results.push(test_result);
|
||||
console.log(["Reporting test", test_result.name, test_result.id]);
|
||||
controller.scope.send_control_message(new messages.TestResult(controller.scope.client_id,
|
||||
test_result.id,
|
||||
test_result.name,
|
||||
test_result.result,
|
||||
test_result.date,
|
||||
test_result.code_under_test));
|
||||
if (typeof(window.__coverage__) !== "undefined" && window.__coverage__ !== null) {
|
||||
console.log(["Reporting coverage", test_result.name, test_result.id]);
|
||||
controller.scope.send_control_message(new messages.Coverage(controller.scope.client_id, window.__coverage__, test_result.id));
|
||||
}
|
||||
controller.changeState(Loading);
|
||||
};
|
||||
_Reporting.prototype.start.transitions = ['Loading'];
|
||||
|
||||
|
||||
_Loading.prototype.start = function (controller) {
|
||||
|
||||
if (controller.scope.current_tests.length === 0) {
|
||||
controller.changeState(Disabled);
|
||||
} else {
|
||||
console.log("Starting test");
|
||||
controller.scope.current_test = controller.scope.current_tests.shift();
|
||||
controller.scope.onSnapshot(controller.scope.current_test.pre_test_snapshot);
|
||||
controller.scope.replay = true;
|
||||
controller.scope.disconnected = true;
|
||||
controller.scope.test_errors = [];
|
||||
controller.scope.test_events = controller.scope.current_test.event_trace.slice();
|
||||
controller.scope.test_events.push(new messages.TestCompleted());
|
||||
controller.scope.reset_coverage();
|
||||
controller.scope.reset_flags();
|
||||
controller.scope.reset_fsm_state();
|
||||
controller.scope.reset_history();
|
||||
controller.scope.reset_toolboxes();
|
||||
controller.changeState(Running);
|
||||
}
|
||||
};
|
||||
_Loading.prototype.start.transitions = ['Running'];
|
||||
|
||||
|
||||
|
||||
_Ready.prototype.onDisableTest = function (controller) {
|
||||
|
||||
controller.changeState(Disabled);
|
||||
};
|
||||
_Ready.prototype.onDisableTest.transitions = ['Disabled'];
|
||||
|
||||
_Ready.prototype.start = function (controller) {
|
||||
|
||||
var load_id = controller.scope.test_result_id_seq();
|
||||
|
||||
console.log(["Reporting Load", load_id]);
|
||||
controller.scope.send_control_message(new messages.TestResult(controller.scope.client_id,
|
||||
load_id,
|
||||
"Load",
|
||||
"passed",
|
||||
new Date().toISOString(),
|
||||
controller.scope.version));
|
||||
if (typeof(window.__coverage__) !== "undefined" && window.__coverage__ !== null) {
|
||||
console.log(["Reporting Load Coverage", load_id]);
|
||||
controller.scope.send_control_message(new messages.Coverage(controller.scope.client_id, window.__coverage__, load_id));
|
||||
}
|
||||
|
||||
controller.changeState(Loading);
|
||||
};
|
||||
_Ready.prototype.start.transitions = ['Loading'];
|
||||
8
awx/ui/client/src/network-ui/test_results.directive.js
Normal file
8
awx/ui/client/src/network-ui/test_results.directive.js
Normal file
@ -0,0 +1,8 @@
|
||||
/* Copyright (c) 2018 Red Hat, Inc. */
|
||||
|
||||
const templateUrl = require('~network-ui/test_results.partial.svg');
|
||||
|
||||
function test_results () {
|
||||
return { restrict: 'A', templateUrl};
|
||||
}
|
||||
exports.test_results = test_results;
|
||||
14
awx/ui/client/src/network-ui/test_results.partial.svg
Normal file
14
awx/ui/client/src/network-ui/test_results.partial.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<g ng-if="test_results.length > 0">
|
||||
<g ng-attr-transform="translate({{graph.width - 300}}, {{graph.height-500}})">
|
||||
<rect class="NetworkUI__test_results" width="280" height="480" x="0" y="0">
|
||||
</rect>
|
||||
<g transform="translate(20, 20)">
|
||||
<text class="NetworkUI__text">Test Results {{version}}</text>
|
||||
<g ng-repeat="result in test_results track by $index">
|
||||
<g ng-attr-transform="translate(0, {{$index * 20 + 20}})">
|
||||
<text class="NetworkUI__text">{{result.name}} - {{result.result}}</text>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
@ -3,6 +3,7 @@ var inherits = require('inherits');
|
||||
var fsm = require('./fsm.js');
|
||||
var messages = require('./messages.js');
|
||||
var util = require('./util.js');
|
||||
var models = require('./models.js');
|
||||
|
||||
function _State () {
|
||||
}
|
||||
@ -539,3 +540,23 @@ _Present.prototype.undo = function(controller) {
|
||||
controller.changeState(Past);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_Present.prototype.onTestCase = function(controller, msg_type, message) {
|
||||
console.log([msg_type, message]);
|
||||
if ('runnable' in message[1]) {
|
||||
if (!message[1].runnable) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
controller.scope.tests.push(new models.Test(message[0],
|
||||
message[1].event_trace,
|
||||
[],
|
||||
message[1].snapshots[0],
|
||||
message[1].snapshots[1]));
|
||||
};
|
||||
|
||||
_Present.prototype.onError = function(controller, msg_type, message) {
|
||||
console.log(["onError", msg_type, message]);
|
||||
throw new Error("ServerError: " + message);
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
var angular = require('angular');
|
||||
|
||||
var tower = angular.module('tower', ['tablesUI', 'networkUI', 'ui.router']);
|
||||
var tower = angular.module('tower', ['networkUI', 'ui.router']);
|
||||
|
||||
tower.config(function($stateProvider, $urlRouterProvider) {
|
||||
|
||||
|
||||
@ -61,6 +61,7 @@
|
||||
"html-loader": "^0.5.1",
|
||||
"html-webpack-harddisk-plugin": "^0.1.0",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"istanbul-instrumenter-loader": "^3.0.0",
|
||||
"jasmine-core": "^2.5.2",
|
||||
"jshint": "^2.9.4",
|
||||
"jshint-stylish": "^2.2.0",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user