diff --git a/awx/main/routing.py b/awx/main/routing.py index abda29bd8c..79a3c84a5a 100644 --- a/awx/main/routing.py +++ b/awx/main/routing.py @@ -1,6 +1,5 @@ from channels.routing import route from awx.network_ui.routing import channel_routing as network_ui_routing -from awx.network_ui_test.routing import channel_routing as network_ui_test_routing channel_routing = [ @@ -11,4 +10,3 @@ channel_routing = [ channel_routing += network_ui_routing -channel_routing += network_ui_test_routing diff --git a/awx/network_ui_test/CONTRIBUTING.md b/awx/network_ui_test/CONTRIBUTING.md deleted file mode 100644 index eb82432402..0000000000 --- a/awx/network_ui_test/CONTRIBUTING.md +++ /dev/null @@ -1,221 +0,0 @@ - -Network UI Test -=============== - -Network UI Test is an event driven test framework for testing the Network UI. -This tool works by setting up the UI with a pre-test run snapshot, replaying a -set of events to the Network UI, catching exceptions and then comparing the -state of the system after the events to a post test run snapshot. Test results -and test code coverage are stored for each test run through the system. This -allows us to determine which lines of test are run during each test and which -lines are run under any test. This can be very helpful during development to -determine that the code is executed as expected given a certain input, to find -code that needs additional tests, and to find code that is not be run under any -of the given inputs (a.k.a. dead code). - -Using this test framework it is fairly easy to achieve 90%+ code coverage under -test with a few days of work recording and crafting tests. - -Test Steps ----------- - -The tests suppported by this test framework perform the following steps: - -* Reset code coverage records -* Recreate a pre-test snapshot on the UI -* Replay a set of events to the UI -* Check for exceptions thrown by the UI -* Check the state of the system based on the post-test snapshot -* Report the pass/fail/error status of the test by test name, code version, and client id -* Report the code coverage per test result -* Repeat for next test - -Test Case Data Schema ---------------------- - -The tests are completely data driven and the data for the test snapshots and events are stored in a JSON -structure in the `TestCase` model under the `test_case_data` field that has the following structure: - - { - "event_trace": [], - "fsm_trace": [], - "snapshots": [ - { - "devices": [], - "inventory_toolbox": [], - "links": [], - "message_id": 4, - "msg_type": "Snapshot", - "order": 0, - "sender": 0, - "trace_id": 2 - }, - { - "devices": [], - "inventory_toolbox": [], - "links": [], - "message_id": 31, - "msg_type": "Snapshot", - "order": 1, - "sender": 0, - "trace_id": 2 - } - ] - } - - -The pre-test snapshot has order 0 and the post-test snapshot has order 1. - - - -Network UI Test Models ----------------------- - -![Models](designs/models.png) - -* FSMTrace - The record of an event that happend during an FSM trace. This is used in the recording phase of test case data. -* EventTrace - The record of an event that happened during an event trace. This is used in the recording phase of test case data. -* Coverage - Per-line test case coverage as returned by istanbul JS code coverage tool. -* TopologySnapshot - A snapshot of the state of the network UI before or after the test was run. -* TestCase - The definition of a test case with a given name using the test case data schema defined above. -* Result - One of passed, failed, errored, skipped, aborted, not run, or blocked. -* TestResult - The record of one test case being run at a certain code location by a client at a certain time. -* CodeUnderTest - The record of what exactly was the code being tested at the time the test was run. - - -Messages --------- - -JSON messages are passed over the `/network_ui/test` websocket between the test client and the test server. -The protocol that is used for all messages is in ABNF (RFC5234): - - - message_type = 'MultipleMessage' / 'MouseEvent' / 'MouseWheelEvent' / 'KeyEvent' / 'StartRecording' / 'StopRecording' / 'ViewPort' / 'FSMTrace' / 'ChannelTrace' / 'Snapshot' / 'EnableTest' / 'DisableTest' / 'StartTest' / 'TestCompleted' / 'TestResult' / 'Coverage' - message_data = '{' 'msg_type' ': ' message_type ', ' key-value *( ', ' key-value ) '}' - message = '[ id , ' posint ']' / '[ topology_id , ' posint ']' / '[' message_type ', ' message_data ']' - -See https://github.com/AndyA/abnfgen/blob/master/andy/json.abnf for the rest of the JSON ABNF. - -See [designs/messages.yml](designs/messages.yml) for the allowable keys and values for each message type. - - - -Loading Tests -------------- - -Tests can be imported from exported data dumps from the `awx-manage datadump` command. From the Tower shell run -the following command: - - awx-manage loaddata /awx_devel/network_ui_test_cases_2018_03_12.json - -This will load the tests from the `network_ui_test_cases_2018_03_12.json` file. - -Exporting Tests ---------------- - -Use the standard Django dumpdata command to dump the test case data to a file: - - awx-manage dumpdata network_ui_test.TestCase > /awx_devel/network_ui_test_cases_YYYY_MM_DD.json - - -Writing Tests Manually or Generating Tests ------------------------------------------- - -Use the empty test case schema above and add messages to the event_trace list. Then upload the -JSON test data to the upload test URL below. - -Recording Tests ---------------- - -Tests can be reruns of previously recorded manual interactions with the network UI. To start a recording -open the JavaScript console and run this command: - - scope.onRecordButton() - -To stop the recording run this command: - - scope.onRecordButton() - -To download the recording use this command: - - scope.onDownloadRecordingButton() - - -After downloading the recording upload the data using the following URL to make a test of it. - -Uploading Tests ---------------- - -Go to the URL: https://SERVER:PORT/network_ui_test/upload_test and use the form to upload test data -and choose a test case name. - -Instrumenting a UI Build ------------------------- - -Code coverage is collected automatically if the UI is built with instrumented code. Add this section to the -rules in awx/ui/build/webpack.base.js to build instrumented code. - - { - test: /\.js$/, - use: { - loader: 'istanbul-instrumenter-loader', - options: { esModules: true } - }, - enforce: 'pre', - include: [ - /src\/network-ui\// - ] - }, - - -Then run: - - make ui-devel - -or: - - make ui-docker - - -To rebuild the code with istanbul instrumentation. - - -Running Tests -------------- - - -To kick off tests in the web browser navigate to the Network UI page under an inventory and open the JavaScript console. -Run this command in the JavaScript console: - - scope.onRunTestsButton(); - - -Building a Coverage Report --------------------------- - -To build a coverage report for the last set of tests that were run as in above. Edit the `tools/Makefile` and change -`SERVER` to match your Tower server. Then run the following command from the `tools` directory: - - make coverage - -This will download all the coverage data from all the test results that were previously recorded and build a coverage -report with istantbul. You can then view the report at the address http://localhost:9000 or http://server:PORT -where you executed the command. - -http://localhost:9000/coverage will contain the coverage for all test cases merged together. -http://localhost:9000/Load/coverage will contain the coverage for just loading the page. -http://localhost:9000/TestX/coverage will contain the coverage for just the TestX test where -TestX is the name of one of the tests run previously. - - - -3.3 Hardening Road Map ----------------------- - -* Add post-test snapshot comparison -* Add FSM trace comparison -* Add an ability to run a single test -* Add an ability to run a subset of the tests -* Add a big red light when event recording is on. - - diff --git a/awx/network_ui_test/__init__.py b/awx/network_ui_test/__init__.py deleted file mode 100644 index d05f93956d..0000000000 --- a/awx/network_ui_test/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Copyright (c) 2017 Red Hat, Inc diff --git a/awx/network_ui_test/consumers.py b/awx/network_ui_test/consumers.py deleted file mode 100644 index a6d3d6a2cf..0000000000 --- a/awx/network_ui_test/consumers.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) 2018 Red Hat, Inc -# In consumers.py -from channels import Group, Channel -from channels.sessions import channel_session -from awx.network_ui.models import Topology, Client -from awx.network_ui.models import TopologyInventory -from awx.network_ui_test.models import FSMTrace, EventTrace, Coverage, TopologySnapshot -from awx.network_ui_test.models import TestCase, TestResult, CodeUnderTest, Result -import urlparse -import logging -from django.utils.dateparse import parse_datetime - - -import json -# Connected to websocket.connect - -logger = logging.getLogger("awx.network_ui_test.consumers") - - -def parse_inventory_id(data): - inventory_id = data.get('inventory_id', ['null']) - try: - inventory_id = int(inventory_id[0]) - except ValueError: - inventory_id = None - if not inventory_id: - inventory_id = None - return inventory_id - - -class TestPersistence(object): - - def parse_message_text(self, message_text, client_id): - data = json.loads(message_text) - if len(data) == 2: - message_type = data.pop(0) - message_value = data.pop(0) - if isinstance(message_value, list): - logger.error("Message has no sender") - return None, None - if isinstance(message_value, dict) and client_id != message_value.get('sender'): - logger.error("client_id mismatch expected: %s actual %s", client_id, message_value.get('sender')) - return None, None - return message_type, message_value - else: - logger.error("Invalid message text") - return None, None - - def handle(self, message): - topology_id = message.get('topology') - assert topology_id is not None, "No topology_id" - client_id = message.get('client') - assert client_id is not None, "No client_id" - message_type, message_value = self.parse_message_text(message['text'], client_id) - if message_type is None: - return - handler = self.get_handler(message_type) - if handler is not None: - try: - handler(message_value, topology_id, client_id) - except Exception: - Group("client-%s" % client_id).send({"text": json.dumps(["Error", "Server Error"])}) - raise - else: - logger.warning("Unsupported message %s: no handler", message_type) - - def get_handler(self, message_type): - return getattr(self, "on{0}".format(message_type), None) - - def onMultipleMessage(self, message_value, topology_id, client_id): - for message in message_value['messages']: - handler = self.get_handler(message['msg_type']) - if handler is not None: - handler(message, topology_id, client_id) - else: - logger.warning("Unsupported message %s", message['msg_type']) - - 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') - - x, y, z = [int(i) for i in xyz.split('.')] - - 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) - - 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() - - - def onCoverage(self, coverage, topology_id, client_id): - 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 - - def onStopRecording(self, recording, topology_id, client_id): - pass - - def write_event(self, event, topology_id, client_id): - if event.get('save', True): - 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 - onTouchEvent = write_event - onMouseWheelEvent = write_event - onKeyEvent = write_event - - def onFSMTrace(self, message_value, diagram_id, client_id): - FSMTrace(trace_session_id=message_value['trace_id'], - fsm_name=message_value['fsm_name'], - from_state=message_value['from_state'], - to_state=message_value['to_state'], - order=message_value['order'], - 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() - - -@channel_session -def ws_connect(message): - # Accept connection - data = urlparse.parse_qs(message.content['query_string']) - inventory_id = parse_inventory_id(data) - topology_ids = list(TopologyInventory.objects.filter(inventory_id=inventory_id).values_list('topology_id', flat=True)) - topology_id = None - if len(topology_ids) > 0: - topology_id = topology_ids[0] - if topology_id is not None: - topology = Topology.objects.get(topology_id=topology_id) - else: - topology = Topology(name="topology", scale=1.0, panX=0, panY=0) - topology.save() - TopologyInventory(inventory_id=inventory_id, topology_id=topology.topology_id).save() - topology_id = topology.topology_id - message.channel_session['topology_id'] = topology_id - 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])}) - send_tests(message.reply_channel) - - -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)]])}) - - -@channel_session -def ws_message(message): - Channel('test_persistence').send({"text": message['text'], - "topology": message.channel_session['topology_id'], - "client": message.channel_session['client_id']}) - - -@channel_session -def ws_disconnect(message): - pass - - diff --git a/awx/network_ui_test/designs/messages.yml b/awx/network_ui_test/designs/messages.yml deleted file mode 100644 index ff0721b535..0000000000 --- a/awx/network_ui_test/designs/messages.yml +++ /dev/null @@ -1,18 +0,0 @@ -messages: - - {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: 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: 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]} - diff --git a/awx/network_ui_test/designs/models.png b/awx/network_ui_test/designs/models.png deleted file mode 100644 index 93327b3c78..0000000000 Binary files a/awx/network_ui_test/designs/models.png and /dev/null differ diff --git a/awx/network_ui_test/designs/models.yml b/awx/network_ui_test/designs/models.yml deleted file mode 100644 index 120f183340..0000000000 --- a/awx/network_ui_test/designs/models.yml +++ /dev/null @@ -1,168 +0,0 @@ -app: awx.network_ui_test -external_models: [] -models: -- fields: - - name: client_id - pk: true - type: AutoField - name: Client - x: -518 - y: 138 -- fields: - - name: fsm_trace_id - pk: true - type: AutoField - - len: 200 - name: fsm_name - type: CharField - - len: 200 - name: from_state - type: CharField - - len: 200 - name: to_state - type: CharField - - len: 200 - name: message_type - type: CharField - - name: client - ref: Client - ref_field: client_id - type: ForeignKey - - default: 0 - name: trace_session_id - type: IntegerField - - default: 0 - name: order - type: IntegerField - name: FSMTrace - x: -872 - y: 507 -- 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 - panY: 189.446959094643 - scaleXY: 0.69 diff --git a/awx/network_ui_test/migrations/0001_initial.py b/awx/network_ui_test/migrations/0001_initial.py deleted file mode 100644 index 1ce75366c1..0000000000 --- a/awx/network_ui_test/migrations/0001_initial.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.7 on 2018-03-06 18:40 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('network_ui', '0001_initial'), - ] - - 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(blank=True, max_length=40)), - ], - ), - migrations.CreateModel( - name='Coverage', - fields=[ - ('coverage_id', models.AutoField(primary_key=True, serialize=False)), - ('coverage_data', models.TextField()), - ], - ), - migrations.CreateModel( - name='EventTrace', - fields=[ - ('event_trace_id', models.AutoField(primary_key=True, serialize=False)), - ('trace_session_id', models.IntegerField(default=0)), - ('event_data', models.TextField()), - ('message_id', models.IntegerField()), - ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')), - ], - ), - migrations.CreateModel( - name='FSMTrace', - fields=[ - ('fsm_trace_id', models.AutoField(primary_key=True, serialize=False)), - ('fsm_name', models.CharField(blank=True, max_length=200)), - ('from_state', models.CharField(blank=True, max_length=200)), - ('to_state', models.CharField(blank=True, max_length=200)), - ('message_type', models.CharField(blank=True, max_length=200)), - ('trace_session_id', models.IntegerField(default=0)), - ('order', models.IntegerField(default=0)), - ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')), - ], - ), - migrations.CreateModel( - name='Result', - fields=[ - ('result_id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, max_length=20)), - ], - ), - migrations.CreateModel( - name='TestCase', - fields=[ - ('test_case_id', models.AutoField(primary_key=True, serialize=False)), - ('name', models.CharField(blank=True, 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()), - ('id', models.IntegerField(default=0)), - ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')), - ('code_under_test', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.CodeUnderTest')), - ('result', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.Result')), - ('test_case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.TestCase')), - ], - ), - 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')), - ], - ), - migrations.AddField( - model_name='coverage', - name='test_result', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.TestResult'), - ), - ] diff --git a/awx/network_ui_test/migrations/0002_auto_20180306_2243.py b/awx/network_ui_test/migrations/0002_auto_20180306_2243.py deleted file mode 100644 index d1a646dcee..0000000000 --- a/awx/network_ui_test/migrations/0002_auto_20180306_2243.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.7 on 2018-03-06 22:43 -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_test', 'Result') - for result in results: - Result.objects.get_or_create(name=result) - - -class Migration(migrations.Migration): - - dependencies = [ - ('network_ui_test', '0001_initial'), - ] - - operations = [ - migrations.RunPython( - code=populate_result_types, - ), - ] diff --git a/awx/network_ui_test/migrations/0003_auto_20180306_2306.py b/awx/network_ui_test/migrations/0003_auto_20180306_2306.py deleted file mode 100644 index 5cf7545908..0000000000 --- a/awx/network_ui_test/migrations/0003_auto_20180306_2306.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.7 on 2018-03-06 23:06 -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_test', 'TestCase') - TestCase.objects.get_or_create(name="Load", test_case_data=json.dumps(dict(runnable=False))) - - -class Migration(migrations.Migration): - - dependencies = [ - ('network_ui_test', '0002_auto_20180306_2243'), - ] - - operations = [ - migrations.RunPython( - code=add_load_test_case, - ), - ] diff --git a/awx/network_ui_test/migrations/__init__.py b/awx/network_ui_test/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/awx/network_ui_test/models.py b/awx/network_ui_test/models.py deleted file mode 100644 index f8f8095c21..0000000000 --- a/awx/network_ui_test/models.py +++ /dev/null @@ -1,73 +0,0 @@ -from django.db import models - - -class FSMTrace(models.Model): - - fsm_trace_id = models.AutoField(primary_key=True,) - fsm_name = models.CharField(max_length=200, blank=True) - from_state = models.CharField(max_length=200, blank=True) - to_state = models.CharField(max_length=200, blank=True) - message_type = models.CharField(max_length=200, blank=True) - client = models.ForeignKey('network_ui.Client',) - trace_session_id = models.IntegerField(default=0,) - order = models.IntegerField(default=0,) - - -class EventTrace(models.Model): - - event_trace_id = models.AutoField(primary_key=True,) - client = models.ForeignKey('network_ui.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('network_ui.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, blank=True) - test_case_data = models.TextField() - - -class Result(models.Model): - - result_id = models.AutoField(primary_key=True,) - name = models.CharField(max_length=20, blank=True) - - -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, blank=True) - - -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('network_ui.Client',) diff --git a/awx/network_ui_test/routing.py b/awx/network_ui_test/routing.py deleted file mode 100644 index f2f27f62d2..0000000000 --- a/awx/network_ui_test/routing.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2017 Red Hat, Inc -from channels.routing import route -from awx.network_ui_test.consumers import ws_connect, ws_message, ws_disconnect, TestPersistence - - -channel_routing = [ - route("websocket.connect", ws_connect, path=r"^/network_ui/test"), - route("websocket.receive", ws_message, path=r"^/network_ui/test"), - route("websocket.disconnect", ws_disconnect, path=r"^/network_ui/test"), - route("test_persistence", TestPersistence().handle), -] - diff --git a/awx/network_ui_test/templates/network_ui_test/upload_test.html b/awx/network_ui_test/templates/network_ui_test/upload_test.html deleted file mode 100644 index f2a851b1c9..0000000000 --- a/awx/network_ui_test/templates/network_ui_test/upload_test.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {% csrf_token %} - {{form}} - -
diff --git a/awx/network_ui_test/tools/Makefile b/awx/network_ui_test/tools/Makefile deleted file mode 100644 index c736a388b9..0000000000 --- a/awx/network_ui_test/tools/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -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} diff --git a/awx/network_ui_test/tools/coverage_report.py b/awx/network_ui_test/tools/coverage_report.py deleted file mode 100755 index 0563f71033..0000000000 --- a/awx/network_ui_test/tools/coverage_report.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Usage: - coverage_report [options] - -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_test/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) - - server = parsed_args[''] - 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__': - sys.exit(main(sys.argv[1:])) diff --git a/awx/network_ui_test/tools/requirements.txt b/awx/network_ui_test/tools/requirements.txt deleted file mode 100644 index bfe4ae8612..0000000000 --- a/awx/network_ui_test/tools/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -docopt diff --git a/awx/network_ui_test/urls.py b/awx/network_ui_test/urls.py deleted file mode 100644 index 0a432d163b..0000000000 --- a/awx/network_ui_test/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017 Red Hat, Inc -from django.conf.urls import url - -from awx.network_ui_test import views - -app_name = 'network_ui_test' -urlpatterns = [ - url(r'^tests$', views.tests, name='tests'), - url(r'^upload_test$', views.upload_test, name='upload_test'), - url(r'^download_coverage/(?P[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'), -] - diff --git a/awx/network_ui_test/views.py b/awx/network_ui_test/views.py deleted file mode 100644 index bdb612d224..0000000000 --- a/awx/network_ui_test/views.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2017 Red Hat, Inc -from django.shortcuts import render -from django import forms -from django.http import JsonResponse, HttpResponse, HttpResponseRedirect -from django.core.exceptions import ObjectDoesNotExist -import yaml - -import json - - -# Create your views here. -from .models import FSMTrace, EventTrace, TopologySnapshot -from .models import TestCase, TestResult, Coverage - - -class FSMTraceForm(forms.Form): - topology_id = forms.IntegerField() - trace_id = forms.IntegerField() - client_id = forms.IntegerField() - - -def download_trace(request): - form = FSMTraceForm(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 = list(FSMTrace.objects.filter(trace_session_id=trace_id, - client_id=client_id).order_by('order').values()) - response = HttpResponse(yaml.safe_dump(data, default_flow_style=False), - 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) - - -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_test/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_test/tests') - else: - form = UploadTestForm() - return render(request, 'network_ui_test/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 - diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 919e471a86..0cbc257ce1 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -279,8 +279,7 @@ INSTALLED_APPS = ( 'awx.ui', 'awx.sso', 'solo', - 'awx.network_ui', - 'awx.network_ui_test', + 'awx.network_ui' ) INTERNAL_IPS = ('127.0.0.1',) diff --git a/awx/ui/client/src/network-ui/network.ui.controller.js b/awx/ui/client/src/network-ui/network.ui.controller.js index 7f5dd3103f..8c9a455165 100644 --- a/awx/ui/client/src/network-ui/network.ui.controller.js +++ b/awx/ui/client/src/network-ui/network.ui.controller.js @@ -35,6 +35,7 @@ var NetworkUIController = function($scope, $scope.api_token = ''; $scope.disconnected = false; + $scope.tests_enabled = false; $scope.topology_id = 0; // Create a web socket to connect to the backend server @@ -54,9 +55,17 @@ var NetworkUIController = function($scope, $scope.control_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/topology?inventory_id=" + $scope.inventory_id, null, {debug: false, reconnectInterval: 300}); - $scope.test_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/test?inventory_id=" + $scope.inventory_id, - null, - {debug: false, reconnectInterval: 300}); + if ($scope.tests_enabled) { + $scope.test_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/test?inventory_id=" + $scope.inventory_id, + null, + {debug: false, reconnectInterval: 300}); + } else { + $scope.test_socket = { + on_message: util.noop, + send: util.noop + }; + } + } else { $scope.control_socket = { on_message: util.noop diff --git a/awx/urls.py b/awx/urls.py index 508ef3075f..57a97fdae5 100644 --- a/awx/urls.py +++ b/awx/urls.py @@ -16,7 +16,6 @@ urlpatterns = [ url(r'^sso/', include('awx.sso.urls', namespace='sso')), url(r'^sso/', include('social_django.urls', namespace='social')), url(r'^network_ui/', include('awx.network_ui.urls', namespace='network_uiui', app_name='network_ui')), - url(r'^network_ui_test/', include('awx.network_ui_test.urls', namespace='network_ui_test', app_name='network_ui_test')), url(r'^(?:api/)?400.html$', handle_400), url(r'^(?:api/)?403.html$', handle_403), url(r'^(?:api/)?404.html$', handle_404),