diff --git a/awx/network_ui/Makefile b/awx/network_ui/Makefile
index 3399013451..80878d207d 100644
--- a/awx/network_ui/Makefile
+++ b/awx/network_ui/Makefile
@@ -6,8 +6,8 @@ all: models admin
models:
jinja2 templates/models.pyt designs/models.yml > models.py
- autopep8 -i models.py
+ autopep8 -i models.py --ignore-local-config --max-line-length 160
admin:
jinja2 templates/admin.pyt designs/models.yml > admin.py
- autopep8 -i admin.py
+ autopep8 -i admin.py --ignore-local-config --max-line-length 160
diff --git a/awx/network_ui/admin.py b/awx/network_ui/admin.py
index 4128d167dd..5a371ca476 100644
--- a/awx/network_ui/admin.py
+++ b/awx/network_ui/admin.py
@@ -14,9 +14,13 @@ from awx.network_ui.models import MessageType
from awx.network_ui.models import Interface
+from awx.network_ui.models import Group
+
+from awx.network_ui.models import GroupDevice
+
class DeviceAdmin(admin.ModelAdmin):
- fields = ('topology', 'name', 'x', 'y', 'id', 'type',)
+ fields = ('topology', 'name', 'x', 'y', 'id', 'type', 'interface_id_seq',)
raw_id_fields = ('topology',)
@@ -32,8 +36,8 @@ admin.site.register(Link, LinkAdmin)
class TopologyAdmin(admin.ModelAdmin):
- fields = ('name', 'scale', 'panX', 'panY',)
- raw_id_fields = ()
+ fields = ('name', 'scale', 'panX', 'panY', 'device_id_seq', 'link_id_seq', 'group_id_seq',)
+ raw_id_fields = ('device_id_seq',)
admin.site.register(Topology, TopologyAdmin)
@@ -69,3 +73,19 @@ class InterfaceAdmin(admin.ModelAdmin):
admin.site.register(Interface, InterfaceAdmin)
+
+
+class GroupAdmin(admin.ModelAdmin):
+ fields = ('id', 'name', 'x1', 'y1', 'x2', 'y2', 'topology',)
+ raw_id_fields = ('id', 'y1', 'x2', 'topology',)
+
+
+admin.site.register(Group, GroupAdmin)
+
+
+class GroupDeviceAdmin(admin.ModelAdmin):
+ fields = ('group', 'device',)
+ raw_id_fields = ('group', 'device',)
+
+
+admin.site.register(GroupDevice, GroupDeviceAdmin)
diff --git a/awx/network_ui/consumers.py b/awx/network_ui/consumers.py
index f9b88899ba..eeadc12f84 100644
--- a/awx/network_ui/consumers.py
+++ b/awx/network_ui/consumers.py
@@ -2,6 +2,8 @@
from channels import Group, Channel
from channels.sessions import channel_session
from awx.network_ui.models import Topology, Device, Link, Client, TopologyHistory, MessageType, Interface
+from awx.network_ui.models import Group as DeviceGroup
+from awx.network_ui.models import GroupDevice as GroupDeviceMap
from awx.network_ui.serializers import yaml_serialize_topology
import urlparse
from django.db.models import Q
@@ -9,10 +11,11 @@ from collections import defaultdict
from django.conf import settings
import math
import random
+import logging
from awx.network_ui.utils import transform_dict
-from pprint import pprint
import dpath.util
+from pprint import pformat
import json
import time
@@ -33,6 +36,8 @@ SPACING = 200
RACK_SPACING = 50
settings.RECORDING = False
+logger = logging.getLogger("awx.network_ui.consumers")
+
def circular_layout(topology_id):
n = Device.objects.filter(topology_id=topology_id).count()
@@ -172,22 +177,17 @@ def tier_layout(topology_id):
edges[device_map[l.from_device.pk]].add(device_map[l.to_device.pk])
edges[device_map[l.to_device.pk]].add(device_map[l.from_device.pk])
- pprint(devices)
similar_connections = defaultdict(list)
for device, connections in edges.iteritems():
similar_connections[tuple(connections)].append(device)
- pprint(dict(**similar_connections))
for connections, from_devices in similar_connections.iteritems():
if len(from_devices) > 0 and from_devices[0].role == "host":
racks.append(from_devices)
- pprint(racks)
- pprint(devices)
-
tiers = defaultdict(list)
for device in devices:
@@ -211,8 +211,6 @@ def tier_layout(topology_id):
for tier in tiers.values():
tier.sort(key=lambda x: x.name)
- pprint(tiers)
-
for device in devices:
print device, getattr(device, 'tier', None)
if getattr(device, 'tier', None) is None:
@@ -227,7 +225,7 @@ def tier_layout(topology_id):
x = 0 - (len(racks) * SPACING) / 2 + j * SPACING
for i, device in enumerate(rack):
device.x = x
- device.y = SPACING * 3 + i * RACK_SPACING
+ device.y = SPACING * 3 + i * RACK_SPACING
device.save()
send_snapshot(Group("topology-%s" % topology_id), topology_id)
@@ -288,6 +286,9 @@ class _Persistence(object):
d.y = device['y']
d.type = device['type']
d.save()
+ (Topology.objects
+ .filter(topology_id=topology_id, device_id_seq__lt=device['id'])
+ .update(device_id_seq=device['id']))
def onDeviceDestroy(self, device, topology_id, client_id):
Device.objects.filter(topology_id=topology_id, id=device['id']).delete()
@@ -313,6 +314,11 @@ class _Persistence(object):
topology_id=topology_id).pk,
id=interface['id'],
defaults=dict(name=interface['name']))
+ (Device.objects
+ .filter(id=interface['device_id'],
+ topology_id=topology_id,
+ interface_id_seq__lt=interface['id'])
+ .update(interface_id_seq=interface['id']))
def onLinkCreate(self, link, topology_id, client_id):
device_map = dict(Device.objects
@@ -326,6 +332,9 @@ class _Persistence(object):
id=link['from_interface_id']).pk,
to_interface_id=Interface.objects.get(device_id=device_map[link['to_device_id']],
id=link['to_interface_id']).pk)
+ (Topology.objects
+ .filter(topology_id=topology_id, link_id_seq__lt=link['id'])
+ .update(link_id_seq=link['id']))
def onLinkDestroy(self, link, topology_id, client_id):
device_map = dict(Device.objects
@@ -370,13 +379,13 @@ class _Persistence(object):
print "Unsupported message ", message['msg_type']
def onDeploy(self, message_value, topology_id, client_id):
- Group("workers").send({"text": json.dumps(["Deploy", topology_id, yaml_serialize_topology(topology_id)])})
+ DeviceGroup("workers").send({"text": json.dumps(["Deploy", topology_id, yaml_serialize_topology(topology_id)])})
def onDestroy(self, message_value, topology_id, client_id):
- Group("workers").send({"text": json.dumps(["Destroy", topology_id])})
+ DeviceGroup("workers").send({"text": json.dumps(["Destroy", topology_id])})
def onDiscover(self, message_value, topology_id, client_id):
- Group("workers").send({"text": json.dumps(["Discover", topology_id, yaml_serialize_topology(topology_id)])})
+ DeviceGroup("workers").send({"text": json.dumps(["Discover", topology_id, yaml_serialize_topology(topology_id)])})
def onLayout(self, message_value, topology_id, client_id):
# circular_layout(topology_id)
@@ -408,6 +417,53 @@ class _Persistence(object):
onMouseWheelEvent = write_event
onKeyEvent = write_event
+ def onGroupCreate(self, group, topology_id, client_id):
+ group = transform_dict(dict(x1='x1',
+ y1='y1',
+ x2='x2',
+ y2='y2',
+ name='name',
+ id='id'), group)
+ d, _ = DeviceGroup.objects.get_or_create(topology_id=topology_id, id=group['id'], defaults=group)
+ d.x1 = group['x1']
+ d.y1 = group['y1']
+ d.x2 = group['x2']
+ d.y2 = group['y2']
+ d.save()
+ (Topology.objects
+ .filter(topology_id=topology_id, group_id_seq__lt=group['id'])
+ .update(group_id_seq=group['id']))
+
+ def onGroupDestroy(self, group, topology_id, client_id):
+ DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).delete()
+
+ def onGroupLabelEdit(self, group, topology_id, client_id):
+ DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).update(name=group['name'])
+
+ def onGroupMove(self, group, topology_id, client_id):
+ DeviceGroup.objects.filter(topology_id=topology_id, id=group['id']).update(x1=group['x1'],
+ y1=group['y1'],
+ x2=group['x2'],
+ y2=group['y2'])
+
+ def onGroupMembership(self, group_membership, topology_id, client_id):
+ members = set(group_membership['members'])
+ group = DeviceGroup.objects.get(topology_id=topology_id, id=group_membership['id'])
+ existing = set(GroupDeviceMap.objects.filter(group=group).values_list('device__id', flat=True))
+ new = members - existing
+ removed = existing - members
+
+ GroupDeviceMap.objects.filter(group__group_id=group.group_id,
+ device__id__in=list(removed)).delete()
+
+ device_map = dict(Device.objects.filter(topology_id=topology_id, id__in=list(new)).values_list('id', 'device_id'))
+ new_entries = []
+ for i in new:
+ new_entries.append(GroupDeviceMap(group=group,
+ device_id=device_map[i]))
+ if new_entries:
+ GroupDeviceMap.objects.bulk_create(new_entries)
+
persistence = _Persistence()
@@ -525,7 +581,9 @@ class _Discovery(object):
def onFacts(self, message, topology_id):
send_updates = False
- print message['key']
+ logger.info("onFacts message key %s", message['key'])
+ logger.info("onFacts message %s", pformat(message))
+ return
name = message['key']
device, created = Device.objects.get_or_create(topology_id=topology_id,
name=name,
@@ -538,11 +596,14 @@ class _Discovery(object):
device.id = device.pk
device.save()
send_updates = True
- print "Created device ", device
+ logger.info("onFacts Created device %s", device)
- interfaces = dpath.util.get(message, '/value/ansible_local/lldp/lldp') or []
+ try:
+ interfaces = dpath.util.get(message, '/value/ansible_local/lldp/lldp')
+ except KeyError:
+ interfaces = []
for interface in interfaces:
- pprint(interface)
+ logger.info("onFacts %s: ", pformat(interface))
for inner_interface in interface.get('interface', []):
name = inner_interface.get('name')
if not name:
@@ -662,7 +723,10 @@ def ws_connect(message):
name='name',
panX='panX',
panY='panY',
- scale='scale'), topology.__dict__)
+ scale='scale',
+ link_id_seq='link_id_seq',
+ device_id_seq='device_id_seq',
+ group_id_seq='group_id_seq'), topology.__dict__)
message.reply_channel.send({"text": json.dumps(["Topology", topology_data])})
send_snapshot(message.reply_channel, topology_id)
@@ -696,9 +760,19 @@ def send_snapshot(channel, topology_id):
'to_device__id',
'from_interface__id',
'to_interface__id'))]
+ groups = list(DeviceGroup.objects
+ .filter(topology_id=topology_id).values())
+ group_map = {g['id']: g for g in groups}
+ for group_id, device_id in GroupDeviceMap.objects.filter(group__topology_id=topology_id).values_list('group__id', 'device__id'):
+ if 'members' not in group_map[group_id]:
+ group_map[group_id]['members'] = [device_id]
+ else:
+ group_map[group_id]['members'].append(device_id)
+
snapshot = dict(sender=0,
devices=devices,
- links=links)
+ links=links,
+ groups=groups)
channel.send({"text": json.dumps(["Snapshot", snapshot])})
diff --git a/awx/network_ui/designs/models.yml b/awx/network_ui/designs/models.yml
index 748c10d779..3a52a4a23f 100644
--- a/awx/network_ui/designs/models.yml
+++ b/awx/network_ui/designs/models.yml
@@ -1,4 +1,4 @@
-app: prototype
+app: awx.network_ui
external_models: []
models:
- display: name
@@ -22,6 +22,9 @@ models:
- len: 200
name: type
type: CharField
+ - default: 0
+ name: interface_id_seq
+ type: IntegerField
name: Device
x: 348
y: 124
@@ -71,6 +74,17 @@ models:
type: FloatField
- name: panY
type: FloatField
+ - default: 0
+ name: device_id_seq
+ ref: Topology
+ ref_field: device_id_seq
+ type: IntegerField
+ - default: 0
+ name: link_id_seq
+ type: IntegerField
+ - default: 0
+ name: group_id_seq
+ type: IntegerField
name: Topology
x: 111
y: 127
@@ -135,6 +149,51 @@ models:
name: Interface
x: 600
y: 243
+- fields:
+ - name: group_id
+ pk: true
+ type: AutoField
+ - name: id
+ ref: Group
+ ref_field: id
+ type: IntegerField
+ - len: 200
+ name: name
+ type: CharField
+ - name: x1
+ type: IntegerField
+ - name: y1
+ ref: Group
+ ref_field: y1
+ type: IntegerField
+ - name: x2
+ ref: Group
+ ref_field: x2
+ type: IntegerField
+ - name: y2
+ type: IntegerField
+ - name: topology
+ ref: Topology
+ ref_field: topology_id
+ type: ForeignKey
+ name: Group
+ x: 907
+ y: 520
+- fields:
+ - name: group_device_id
+ pk: true
+ type: AutoField
+ - name: group
+ ref: GroupDevice
+ ref_field: group
+ type: ForeignKey
+ - name: device
+ ref: Device
+ ref_field: device_id
+ type: ForeignKey
+ name: GroupDevice
+ x: 631
+ y: 561
modules: []
view:
panX: 213.72955551921206
diff --git a/awx/network_ui/migrations/0012_auto_20170706_1526.py b/awx/network_ui/migrations/0012_auto_20170706_1526.py
new file mode 100644
index 0000000000..13ef7b27a5
--- /dev/null
+++ b/awx/network_ui/migrations/0012_auto_20170706_1526.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('network_ui', '0011_link_name'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='device',
+ name='interface_id_seq',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AddField(
+ model_name='topology',
+ name='device_id_seq',
+ field=models.IntegerField(default=0, verbose_name=b'Topology'),
+ ),
+ migrations.AddField(
+ model_name='topology',
+ name='link_id_seq',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/awx/network_ui/migrations/0013_auto_20170710_1840.py b/awx/network_ui/migrations/0013_auto_20170710_1840.py
new file mode 100644
index 0000000000..16ba192aa5
--- /dev/null
+++ b/awx/network_ui/migrations/0013_auto_20170710_1840.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('network_ui', '0012_auto_20170706_1526'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Group',
+ fields=[
+ ('group_id', models.AutoField(serialize=False, primary_key=True)),
+ ('id', models.IntegerField(verbose_name=b'Group')),
+ ('name', models.CharField(max_length=200)),
+ ('x1', models.IntegerField()),
+ ('y1', models.IntegerField(verbose_name=b'Group')),
+ ('x2', models.IntegerField(verbose_name=b'Group')),
+ ('y2', models.IntegerField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GroupDevice',
+ fields=[
+ ('group_device_id', models.AutoField(serialize=False, primary_key=True)),
+ ('device', models.ForeignKey(to='network_ui.Device')),
+ ('group', models.ForeignKey(to='network_ui.GroupDevice')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='topology',
+ name='group_id_seq',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/awx/network_ui/migrations/0014_group_topology.py b/awx/network_ui/migrations/0014_group_topology.py
new file mode 100644
index 0000000000..e3e0de45ed
--- /dev/null
+++ b/awx/network_ui/migrations/0014_group_topology.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('network_ui', '0013_auto_20170710_1840'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='group',
+ name='topology',
+ field=models.ForeignKey(default=1, to='network_ui.Topology'),
+ preserve_default=False,
+ ),
+ ]
diff --git a/awx/network_ui/migrations/0015_auto_20170710_1937.py b/awx/network_ui/migrations/0015_auto_20170710_1937.py
new file mode 100644
index 0000000000..21b2d7db03
--- /dev/null
+++ b/awx/network_ui/migrations/0015_auto_20170710_1937.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('network_ui', '0014_group_topology'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='groupdevice',
+ name='group',
+ field=models.ForeignKey(to='network_ui.Group'),
+ ),
+ ]
diff --git a/awx/network_ui/models.py b/awx/network_ui/models.py
index 0256e7494e..72420130fb 100644
--- a/awx/network_ui/models.py
+++ b/awx/network_ui/models.py
@@ -5,11 +5,12 @@ class Device(models.Model):
device_id = models.AutoField(primary_key=True,)
topology = models.ForeignKey('Topology',)
- name = models.CharField(max_length=200, )
+ name = models.CharField(max_length=200,)
x = models.IntegerField()
y = models.IntegerField()
id = models.IntegerField()
- type = models.CharField(max_length=200, )
+ type = models.CharField(max_length=200,)
+ interface_id_seq = models.IntegerField(default=0)
def __unicode__(self):
return self.name
@@ -18,21 +19,24 @@ class Device(models.Model):
class Link(models.Model):
link_id = models.AutoField(primary_key=True,)
- from_device = models.ForeignKey('Device', related_name='from_link', )
- to_device = models.ForeignKey('Device', related_name='to_link', )
- from_interface = models.ForeignKey('Interface', related_name='from_link', )
- to_interface = models.ForeignKey('Interface', related_name='to_link', )
+ from_device = models.ForeignKey('Device', related_name='from_link',)
+ to_device = models.ForeignKey('Device', related_name='to_link',)
+ from_interface = models.ForeignKey('Interface', related_name='from_link',)
+ to_interface = models.ForeignKey('Interface', related_name='to_link',)
id = models.IntegerField()
- name = models.CharField(max_length=200, )
+ name = models.CharField(max_length=200,)
class Topology(models.Model):
topology_id = models.AutoField(primary_key=True,)
- name = models.CharField(max_length=200, )
+ name = models.CharField(max_length=200,)
scale = models.FloatField()
panX = models.FloatField()
panY = models.FloatField()
+ device_id_seq = models.IntegerField('Topology', default=0)
+ link_id_seq = models.IntegerField(default=0)
+ group_id_seq = models.IntegerField(default=0)
def __unicode__(self):
return self.name
@@ -57,7 +61,7 @@ class TopologyHistory(models.Model):
class MessageType(models.Model):
message_type_id = models.AutoField(primary_key=True,)
- name = models.CharField(max_length=200, )
+ name = models.CharField(max_length=200,)
def __unicode__(self):
return self.name
@@ -67,8 +71,27 @@ class Interface(models.Model):
interface_id = models.AutoField(primary_key=True,)
device = models.ForeignKey('Device',)
- name = models.CharField(max_length=200, )
+ name = models.CharField(max_length=200,)
id = models.IntegerField()
def __unicode__(self):
return self.name
+
+
+class Group(models.Model):
+
+ group_id = models.AutoField(primary_key=True,)
+ id = models.IntegerField('Group',)
+ name = models.CharField(max_length=200,)
+ x1 = models.IntegerField()
+ y1 = models.IntegerField('Group',)
+ x2 = models.IntegerField('Group',)
+ y2 = models.IntegerField()
+ topology = models.ForeignKey('Topology',)
+
+
+class GroupDevice(models.Model):
+
+ group_device_id = models.AutoField(primary_key=True,)
+ group = models.ForeignKey('Group',)
+ device = models.ForeignKey('Device',)
diff --git a/awx/network_ui/serializers.py b/awx/network_ui/serializers.py
index 4d1f533c20..0290d4ee42 100644
--- a/awx/network_ui/serializers.py
+++ b/awx/network_ui/serializers.py
@@ -1,8 +1,9 @@
-from awx.network_ui.models import Topology, Device, Link, Interface
+from awx.network_ui.models import Topology, Device, Link, Interface, Group, GroupDevice
from django.db.models import Q
import yaml
import json
+from collections import defaultdict
NetworkAnnotatedInterface = Interface.objects.values('name',
'id',
@@ -17,13 +18,29 @@ NetworkAnnotatedInterface = Interface.objects.values('name',
def topology_data(topology_id):
data = dict(devices=[],
- links=[])
+ links=[],
+ groups=[])
topology = Topology.objects.get(pk=topology_id)
data['name'] = topology.name
data['topology_id'] = topology_id
+ groups = list(Group.objects.filter(topology_id=topology_id).values())
+ group_devices = GroupDevice.objects.filter(group__topology_id=topology_id).values('group_id', 'device_id', 'device__name', 'group__name')
+ group_device_map = defaultdict(list)
+
+ for group_device in group_devices:
+ group_device_map[group_device['group_id']].append(group_device)
+
+ device_group_map = defaultdict(list)
+ for group_device in group_devices:
+ device_group_map[group_device['device_id']].append(group_device)
+
+ for group in groups:
+ group['members'] = [x['device__name'] for x in group_device_map[group['group_id']]]
+ data['groups'] = groups
+
links = list(Link.objects
.filter(Q(from_device__topology_id=topology_id) |
Q(to_device__topology_id=topology_id)))
@@ -43,7 +60,8 @@ def topology_data(topology_id):
x=device.x,
y=device.y,
id=device.id,
- interfaces=interfaces))
+ interfaces=interfaces,
+ groups=[x['group__name'] for x in device_group_map[device.device_id]]))
for link in links:
data['links'].append(dict(from_device=link.from_device.name,
@@ -54,7 +72,9 @@ def topology_data(topology_id):
to_device_id=link.to_device.id,
from_interface_id=link.from_interface.id,
to_interface_id=link.to_interface.id,
+ name=link.name,
network=link.pk))
+
return data
diff --git a/awx/network_ui/static/network_ui/CONTRIBUTING.md b/awx/network_ui/static/network_ui/CONTRIBUTING.md
new file mode 100644
index 0000000000..2b835bdae2
--- /dev/null
+++ b/awx/network_ui/static/network_ui/CONTRIBUTING.md
@@ -0,0 +1,10 @@
+
+
+To build the UI:
+
+make
+
+
+To push the UI to tower code base:
+
+make deploy
diff --git a/awx/network_ui/static/network_ui/Makefile b/awx/network_ui/static/network_ui/Makefile
index 6a5f3bfbe6..b24ed19334 100644
--- a/awx/network_ui/static/network_ui/Makefile
+++ b/awx/network_ui/static/network_ui/Makefile
@@ -1,15 +1,18 @@
-.PHONY: all main lint lessc
+.PHONY: all main lint lessc install simple-server deploy
-all: clean lessc lint main istanbul
+all: clean install lessc lint main
clean:
rm -rf src-instrumented
rm -f js/bundle.js
+ rm -f js/vendor.bundle.js
rm -f css/style.css
-main:
- webpack src/main.js js/bundle.js
- cp vendor/*.js js/
+install:
+ npm i
+
+main: install
+ webpack
lint:
jshint --verbose src/*js
@@ -23,3 +26,11 @@ istanbul:
cp index.html index-instrumented.html
sed -i "s/bundle.js/bundle-instrumented.js/g" index-instrumented.html
cp vendor/*.js js/
+
+
+simple-server:
+ python -m SimpleHTTPServer
+
+
+deploy: main
+ rsync -av src/ ../../../../awx/ui/client/src/network_ui/
diff --git a/awx/network_ui/static/network_ui/designs/group.yml b/awx/network_ui/static/network_ui/designs/group.yml
new file mode 100644
index 0000000000..c5b3f146c8
--- /dev/null
+++ b/awx/network_ui/static/network_ui/designs/group.yml
@@ -0,0 +1,98 @@
+finite_state_machine_id: 102
+name: fsm
+states:
+- id: 1
+ label: Resize
+ x: 571
+ y: 911
+- id: 2
+ label: Start
+ x: 744
+ y: 69
+- id: 3
+ label: CornerSelected
+ x: 359
+ y: 682
+- id: 4
+ label: Selected1
+ x: 839
+ y: 640
+- id: 5
+ label: Selected3
+ x: 1528
+ y: 360
+- id: 6
+ label: Move
+ x: 1297
+ y: 786
+- id: 7
+ label: Ready
+ x: 740
+ y: 324
+- id: 8
+ label: EditLabel
+ x: 1056
+ y: 148
+- id: 9
+ label: Selected2
+ x: 1179
+ y: 435
+- id: 10
+ label: Placing
+ x: 410
+ y: 295
+transitions:
+- from_state: Ready
+ label: onMouseDown
+ to_state: Selected1
+- from_state: EditLabel
+ label: onMouseDown
+ to_state: Ready
+- from_state: Selected2
+ label: onMouseDown
+ to_state: Ready
+- from_state: Selected2
+ label: onMouseDown
+ to_state: Selected3
+- from_state: Selected1
+ label: onMouseUp
+ to_state: Selected2
+- from_state: Move
+ label: onMouseUp
+ to_state: Selected2
+- from_state: Selected1
+ label: onMouseMove
+ to_state: Move
+- from_state: Start
+ label: start
+ to_state: Ready
+- from_state: Ready
+ label: onMouseDown
+ to_state: CornerSelected
+- from_state: Selected3
+ label: onMouseMove
+ to_state: Move
+- from_state: Selected3
+ label: onMouseUp
+ to_state: EditLabel
+- from_state: Ready
+ label: onNewGroup
+ to_state: Placing
+- from_state: Placing
+ label: onMouseDown
+ to_state: CornerSelected
+- from_state: CornerSelected
+ label: onMouseMove
+ to_state: Resize
+- from_state: Resize
+ label: onMouseUp
+ to_state: Selected1
+- from_state: Selected2
+ label: onNewGroup
+ to_state: Ready
+- from_state: CornerSelected
+ label: onMouseUp
+ to_state: Selected1
+- from_state: Move
+ label: onMouseDown
+ to_state: Selected1
diff --git a/awx/network_ui/static/network_ui/designs/hotkeys.yml b/awx/network_ui/static/network_ui/designs/hotkeys.yml
new file mode 100644
index 0000000000..95fd1d3982
--- /dev/null
+++ b/awx/network_ui/static/network_ui/designs/hotkeys.yml
@@ -0,0 +1,25 @@
+finite_state_machine_id: 113
+name: hotkeys
+states:
+- id: 2
+ label: Enabled
+ x: 585
+ y: 396
+- id: 1
+ label: Start
+ x: 585
+ y: 160
+- id: 3
+ label: Disabled
+ x: 331
+ y: 408
+transitions:
+- from_state: Enabled
+ label: onDisable
+ to_state: Disabled
+- from_state: Disabled
+ label: onEnable
+ to_state: Enabled
+- from_state: Start
+ label: start
+ to_state: Enabled
diff --git a/awx/network_ui/static/network_ui/designs/move.yml b/awx/network_ui/static/network_ui/designs/move.yml
index a3a18757cc..4425aaa4d0 100644
--- a/awx/network_ui/static/network_ui/designs/move.yml
+++ b/awx/network_ui/static/network_ui/designs/move.yml
@@ -1,36 +1,38 @@
-app: move
-panX: 285.92999999999995
-panY: -151.52999999999997
-scaleXY: 0.8700000000000001
+finite_state_machine_id: 106
+name: move
states:
-- label: Start
- size: 100
+- id: 1
+ label: Start
x: 533
y: 121
-- label: Ready
- size: 100
+- id: 2
+ label: Ready
x: 531
y: 320
-- label: Selected1
- size: 100
+- id: 3
+ label: Selected1
x: 226
y: 325
-- label: Selected2
- size: 100
+- id: 4
+ label: Selected2
x: 230
y: 582
-- label: Move
- size: 100
+- id: 5
+ label: Move
x: -54
y: 587
-- label: EditLabel
- size: 100
- x: 535.7126436781609
- y: 583.367816091954
-- label: Selected3
- size: 100
- x: 231.11494252873567
- y: 867.2758620689654
+- id: 6
+ label: EditLabel
+ x: 566
+ y: 677
+- id: 7
+ label: Selected3
+ x: 175
+ y: 886
+- id: 8
+ label: Placing
+ x: 92
+ y: 82
transitions:
- from_state: Start
label: start
@@ -49,6 +51,9 @@ transitions:
to_state: Ready
- from_state: Move
label: onMouseUp
+ to_state: Selected1
+- from_state: EditLabel
+ label: onKeyDown
to_state: Selected2
- from_state: EditLabel
label: onMouseDown
@@ -62,3 +67,21 @@ transitions:
- from_state: Selected3
label: onMouseUp
to_state: EditLabel
+- from_state: Placing
+ label: onMouseMove
+ to_state: Move
+- from_state: Ready
+ label: onNewDevice
+ to_state: Placing
+- from_state: Placing
+ label: onMouseDown
+ to_state: Selected1
+- from_state: Move
+ label: onMouseDown
+ to_state: Selected1
+- from_state: Selected2
+ label: onNewDevice
+ to_state: Ready
+- from_state: Selected2
+ label: onKeyDown
+ to_state: Ready
diff --git a/awx/network_ui/static/network_ui/designs/null.yml b/awx/network_ui/static/network_ui/designs/null.yml
new file mode 100644
index 0000000000..d8e3e8dd06
--- /dev/null
+++ b/awx/network_ui/static/network_ui/designs/null.yml
@@ -0,0 +1,15 @@
+finite_state_machine_id: 114
+name: fsm
+states:
+- id: 1
+ label: Start
+ x: 391
+ y: 132
+- id: 2
+ label: Ready
+ x: 402
+ y: 346
+transitions:
+- from_state: Start
+ label: start
+ to_state: Ready
diff --git a/awx/network_ui/static/network_ui/designs/time.yml b/awx/network_ui/static/network_ui/designs/time.yml
index b3953607c1..4c431fabd3 100644
--- a/awx/network_ui/static/network_ui/designs/time.yml
+++ b/awx/network_ui/static/network_ui/designs/time.yml
@@ -1,27 +1,43 @@
-app: time
-panX: 0
-panY: 0
-scaleXY: 1
+finite_state_machine_id: 89
+name: src/time
states:
-- label: Start
- size: 100
- x: 634
- y: 117
-- label: Present
- size: 100
- x: 632
- y: 379
-- label: Past
- size: 100
- x: 367
- y: 369
+- id: 3
+ label: Present
+ x: 256
+ y: 123
+- id: 2
+ label: Start
+ x: 245
+ y: -161
+- id: 1
+ label: Past
+ x: -115
+ y: 129
transitions:
+- from_state: Past
+ label: onMessage
+ to_state: Present
+- from_state: Past
+ label: onRedo
+ to_state: Present
+- from_state: Past
+ label: onMouseWheel
+ to_state: Present
+- from_state: Past
+ label: onKeyDown
+ to_state: Present
- from_state: Start
label: start
to_state: Present
+- from_state: Present
+ label: onMessage
+ to_state: Past
+- from_state: Present
+ label: onUndo
+ to_state: Past
- from_state: Present
label: onMouseWheel
to_state: Past
-- from_state: Past
- label: onMouseWheel
- to_state: Present
+- from_state: Present
+ label: onKeyDown
+ to_state: Past
diff --git a/awx/network_ui/static/network_ui/index.html b/awx/network_ui/static/network_ui/index.html
index 852bee9ae8..e5cc3d53ae 100644
--- a/awx/network_ui/static/network_ui/index.html
+++ b/awx/network_ui/static/network_ui/index.html
@@ -1,111 +1,12 @@
-
+
-
-
-
-
-
+
-
-
+
+
diff --git a/awx/network_ui/static/network_ui/index2.html b/awx/network_ui/static/network_ui/index2.html
new file mode 100644
index 0000000000..0fc5f099c9
--- /dev/null
+++ b/awx/network_ui/static/network_ui/index2.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awx/network_ui/static/network_ui/package.json b/awx/network_ui/static/network_ui/package.json
index 164c171b5e..02379a0b65 100644
--- a/awx/network_ui/static/network_ui/package.json
+++ b/awx/network_ui/static/network_ui/package.json
@@ -9,13 +9,18 @@
"author": "Ben Thomasson",
"license": "ISC",
"dependencies": {
+ "angular": "~1.6.2",
+ "angular-ui-router": "",
"webpack": "",
"browserify": "",
"inherits": "",
"require": "",
"jshint": "",
"less": "",
- "mathjs": ""
+ "mathjs": "",
+ "reconnectingwebsocket": "^1.0.0",
+ "hamsterjs": "~1.1.2",
+ "angular-mousewheel": "~1.0.5"
},
"devDependencies": {
"eslint": "^3.17.1",
diff --git a/awx/network_ui/static/network_ui/run.sh b/awx/network_ui/static/network_ui/run.sh
index ee8b903bbe..717246ffd3 100755
--- a/awx/network_ui/static/network_ui/run.sh
+++ b/awx/network_ui/static/network_ui/run.sh
@@ -1,3 +1,3 @@
#!/bin/bash -ex
-python -m SimpleHTTPServer
+python -m SimpleHTTPServer 8080
diff --git a/awx/network_ui/static/network_ui/src/button.directive.js b/awx/network_ui/static/network_ui/src/button.directive.js
new file mode 100644
index 0000000000..ea1515a585
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/button.directive.js
@@ -0,0 +1,5 @@
+
+function button () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/button.html' };
+}
+exports.button = button;
diff --git a/awx/network_ui/static/network_ui/src/button.js b/awx/network_ui/static/network_ui/src/button.js
index 276a1d7a41..1d9ea3a1bf 100644
--- a/awx/network_ui/static/network_ui/src/button.js
+++ b/awx/network_ui/static/network_ui/src/button.js
@@ -55,8 +55,8 @@ _Start.prototype.start.transitions = ['Ready'];
_Clicked.prototype.start = function (controller) {
- controller.scope.callback(controller.scope);
controller.scope.is_pressed = false;
+ controller.scope.callback(controller.scope);
controller.changeState(Ready);
};
_Clicked.prototype.start.transitions = ['Ready'];
diff --git a/awx/network_ui/static/network_ui/src/buttons.js b/awx/network_ui/static/network_ui/src/buttons.js
index 4e2017d5dd..d63f73b636 100644
--- a/awx/network_ui/static/network_ui/src/buttons.js
+++ b/awx/network_ui/static/network_ui/src/buttons.js
@@ -33,7 +33,7 @@ exports.ButtonPressed = ButtonPressed;
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
var i = 0;
- var buttons = controller.scope.buttons;
+ var buttons = controller.scope.all_buttons;
var button = null;
for (i = 0; i < buttons.length; i++) {
button = buttons[i];
@@ -58,7 +58,7 @@ _Ready.prototype.onMouseMove = function (controller, msg_type, $event) {
if (!controller.scope.hide_buttons) {
var i = 0;
- var buttons = controller.scope.buttons;
+ var buttons = controller.scope.all_buttons;
var button = null;
for (i = 0; i < buttons.length; i++) {
button = buttons[i];
@@ -85,7 +85,7 @@ _Start.prototype.start.transitions = ['Ready'];
_ButtonPressed.prototype.onMouseUp = function (controller, msg_type, $event) {
var i = 0;
- var buttons = controller.scope.buttons;
+ var buttons = controller.scope.all_buttons;
var button = null;
for (i = 0; i < buttons.length; i++) {
button = buttons[i];
diff --git a/awx/network_ui/static/network_ui/src/cursor.directive.js b/awx/network_ui/static/network_ui/src/cursor.directive.js
new file mode 100644
index 0000000000..27c8982ca5
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/cursor.directive.js
@@ -0,0 +1,5 @@
+
+function cursor () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/cursor.html' };
+}
+exports.cursor = cursor;
diff --git a/awx/network_ui/static/network_ui/src/debug.directive.js b/awx/network_ui/static/network_ui/src/debug.directive.js
new file mode 100644
index 0000000000..d26f979bc6
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/debug.directive.js
@@ -0,0 +1,6 @@
+
+function debug () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/debug.html' };
+}
+
+exports.debug = debug;
diff --git a/awx/network_ui/static/network_ui/src/default.directive.js b/awx/network_ui/static/network_ui/src/default.directive.js
new file mode 100644
index 0000000000..7c0c865ad2
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/default.directive.js
@@ -0,0 +1,5 @@
+
+function defaultd () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/default.html' };
+}
+exports.defaultd = defaultd;
diff --git a/awx/network_ui/static/network_ui/src/group.directive.js b/awx/network_ui/static/network_ui/src/group.directive.js
new file mode 100644
index 0000000000..78ea434658
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/group.directive.js
@@ -0,0 +1,5 @@
+
+function group () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/group.html' };
+}
+exports.group = group;
diff --git a/awx/network_ui/static/network_ui/src/group.js b/awx/network_ui/static/network_ui/src/group.js
new file mode 100644
index 0000000000..f0bcdd5cae
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/group.js
@@ -0,0 +1,527 @@
+var inherits = require('inherits');
+var fsm = require('./fsm.js');
+var models = require('./models.js');
+var messages = require('./messages.js');
+
+function _State () {
+}
+inherits(_State, fsm._State);
+
+
+function _Resize () {
+ this.name = 'Resize';
+}
+inherits(_Resize, _State);
+var Resize = new _Resize();
+exports.Resize = Resize;
+
+function _Start () {
+ this.name = 'Start';
+}
+inherits(_Start, _State);
+var Start = new _Start();
+exports.Start = Start;
+
+function _CornerSelected () {
+ this.name = 'CornerSelected';
+}
+inherits(_CornerSelected, _State);
+var CornerSelected = new _CornerSelected();
+exports.CornerSelected = CornerSelected;
+
+function _Selected1 () {
+ this.name = 'Selected1';
+}
+inherits(_Selected1, _State);
+var Selected1 = new _Selected1();
+exports.Selected1 = Selected1;
+
+function _Selected3 () {
+ this.name = 'Selected3';
+}
+inherits(_Selected3, _State);
+var Selected3 = new _Selected3();
+exports.Selected3 = Selected3;
+
+function _Move () {
+ this.name = 'Move';
+}
+inherits(_Move, _State);
+var Move = new _Move();
+exports.Move = Move;
+
+function _Ready () {
+ this.name = 'Ready';
+}
+inherits(_Ready, _State);
+var Ready = new _Ready();
+exports.Ready = Ready;
+
+function _EditLabel () {
+ this.name = 'EditLabel';
+}
+inherits(_EditLabel, _State);
+var EditLabel = new _EditLabel();
+exports.EditLabel = EditLabel;
+
+function _Selected2 () {
+ this.name = 'Selected2';
+}
+inherits(_Selected2, _State);
+var Selected2 = new _Selected2();
+exports.Selected2 = Selected2;
+
+function _Placing () {
+ this.name = 'Placing';
+}
+inherits(_Placing, _State);
+var Placing = new _Placing();
+exports.Placing = Placing;
+
+
+_Resize.prototype.onMouseUp = function (controller, msg_type, $event) {
+
+ controller.changeState(Selected1);
+ controller.handle_message(msg_type, $event);
+
+};
+_Resize.prototype.onMouseUp.transitions = ['Selected1'];
+
+_Resize.prototype.onMouseMove = function (controller) {
+
+ var groups = controller.scope.selected_groups;
+
+ var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
+ var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
+ var i = 0;
+ var j = 0;
+ var membership_old_new = [];
+ var previous_x1, previous_y1, previous_x2, previous_y2;
+ for (i = 0; i < groups.length; i++) {
+ previous_x1 = groups[i].x1;
+ previous_y1 = groups[i].y1;
+ previous_x2 = groups[i].x2;
+ previous_y2 = groups[i].y2;
+ if (groups[i].selected_corner === models.TOP_LEFT) {
+ groups[i].x1 = groups[i].x1 + diffX;
+ groups[i].y1 = groups[i].y1 + diffY;
+ }
+ if (groups[i].selected_corner === models.BOTTOM_RIGHT) {
+ groups[i].x2 = groups[i].x2 + diffX;
+ groups[i].y2 = groups[i].y2 + diffY;
+ }
+ if (groups[i].selected_corner === models.TOP_RIGHT) {
+ groups[i].x2 = groups[i].x2 + diffX;
+ groups[i].y1 = groups[i].y1 + diffY;
+ }
+ if (groups[i].selected_corner === models.BOTTOM_LEFT) {
+ groups[i].x1 = groups[i].x1 + diffX;
+ groups[i].y2 = groups[i].y2 + diffY;
+ }
+
+ membership_old_new = groups[i].update_membership(controller.scope.devices);
+ for(j = 0; j < membership_old_new[0].length; j++) {
+ membership_old_new[0][j].selected = false;
+ }
+ for(j = 0; j < membership_old_new[1].length; j++) {
+ membership_old_new[1][j].selected = true;
+ }
+
+ controller.scope.send_control_message(new messages.GroupMove(controller.scope.client_id,
+ groups[i].id,
+ groups[i].x1,
+ groups[i].y1,
+ groups[i].x2,
+ groups[i].y2,
+ previous_x1,
+ previous_y1,
+ previous_x2,
+ previous_y2));
+ controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
+ groups[i].id,
+ membership_old_new[2]));
+ }
+ controller.scope.pressedScaledX = controller.scope.scaledX;
+ controller.scope.pressedScaledY = controller.scope.scaledY;
+};
+
+_Resize.prototype.end = function (controller) {
+
+ var groups = controller.scope.selected_groups;
+
+ var i = 0;
+ var j = 0;
+ for (i = 0; i < groups.length; i++) {
+ for(j = 0; j < groups[i].devices.length; j++) {
+ groups[i].devices[j].selected = false;
+ }
+ }
+};
+
+
+_Start.prototype.start = function (controller) {
+
+ controller.changeState(Ready);
+
+};
+_Start.prototype.start.transitions = ['Ready'];
+
+_CornerSelected.prototype.start = function (controller) {
+
+ var groups = controller.scope.selected_groups;
+ var i = 0;
+ var x = controller.scope.scaledX;
+ var y = controller.scope.scaledY;
+ for (i = 0; i < groups.length; i++) {
+ groups[i].selected_corner = groups[i].select_corner(x, y);
+ }
+};
+
+_CornerSelected.prototype.onMouseMove = function (controller) {
+
+ controller.changeState(Resize);
+};
+_CornerSelected.prototype.onMouseMove.transitions = ['Resize'];
+
+_CornerSelected.prototype.onMouseUp = function (controller, msg_type, $event) {
+
+ controller.changeState(Selected1);
+ controller.handle_message(msg_type, $event);
+};
+_CornerSelected.prototype.onMouseUp.transitions = ['Selected1'];
+
+
+
+_Selected1.prototype.onMouseMove = function (controller) {
+
+ controller.changeState(Move);
+
+};
+_Selected1.prototype.onMouseMove.transitions = ['Move'];
+
+_Selected1.prototype.onMouseUp = function (controller) {
+
+ controller.changeState(Selected2);
+};
+_Selected1.prototype.onMouseUp.transitions = ['Selected2'];
+
+
+
+_Selected3.prototype.onMouseMove = function (controller) {
+
+ controller.changeState(Move);
+
+};
+_Selected3.prototype.onMouseMove.transitions = ['Move'];
+
+_Selected3.prototype.onMouseUp = function (controller) {
+
+ controller.changeState(EditLabel);
+
+};
+_Selected3.prototype.onMouseUp.transitions = ['EditLabel'];
+
+
+_Move.prototype.onMouseMove = function (controller) {
+
+ var groups = controller.scope.selected_groups;
+
+ var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
+ var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
+ var i = 0;
+ var j = 0;
+ var membership_old_new = [];
+ var previous_x1, previous_y1, previous_x2, previous_y2;
+ for (i = 0; i < groups.length; i++) {
+ previous_x1 = groups[i].x1;
+ previous_y1 = groups[i].y1;
+ previous_x2 = groups[i].x2;
+ previous_y2 = groups[i].y2;
+ groups[i].x1 = groups[i].x1 + diffX;
+ groups[i].y1 = groups[i].y1 + diffY;
+ groups[i].x2 = groups[i].x2 + diffX;
+ groups[i].y2 = groups[i].y2 + diffY;
+
+ membership_old_new = groups[i].update_membership(controller.scope.devices);
+ for(j = 0; j < membership_old_new[0].length; j++) {
+ membership_old_new[0][j].selected = false;
+ }
+ for(j = 0; j < membership_old_new[1].length; j++) {
+ membership_old_new[1][j].selected = true;
+ }
+
+ controller.scope.send_control_message(new messages.GroupMove(controller.scope.client_id,
+ groups[i].id,
+ groups[i].x1,
+ groups[i].y1,
+ groups[i].x2,
+ groups[i].y2,
+ previous_x1,
+ previous_y1,
+ previous_x2,
+ previous_y2));
+ controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
+ groups[i].id,
+ membership_old_new[2]));
+ }
+ controller.scope.pressedScaledX = controller.scope.scaledX;
+ controller.scope.pressedScaledY = controller.scope.scaledY;
+};
+
+_Move.prototype.onMouseUp = function (controller) {
+
+ controller.changeState(Selected2);
+
+};
+_Move.prototype.onMouseUp.transitions = ['Selected2'];
+
+_Move.prototype.onMouseDown = function (controller) {
+
+ controller.changeState(Selected1);
+};
+_Move.prototype.onMouseDown.transitions = ['Selected1'];
+
+_Move.prototype.end = function (controller) {
+
+ var groups = controller.scope.selected_groups;
+
+ var i = 0;
+ var j = 0;
+ for (i = 0; i < groups.length; i++) {
+ for(j = 0; j < groups[i].devices.length; j++) {
+ groups[i].devices[j].selected = false;
+ }
+ }
+};
+
+
+_Ready.prototype.onMouseMove = function (controller, msg_type, $event) {
+
+ if (controller.scope.hide_groups) {
+ controller.next_controller.handle_message(msg_type, $event);
+ return;
+ }
+
+ var i = 0;
+
+ for (i = 0; i < controller.scope.groups.length; i++) {
+ controller.scope.groups[i].update_hightlighted(controller.scope.scaledX, controller.scope.scaledY);
+ }
+
+ controller.next_controller.handle_message(msg_type, $event);
+};
+
+
+_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
+
+ if (controller.scope.hide_groups) {
+ controller.next_controller.handle_message(msg_type, $event);
+ return;
+ }
+
+ //
+ var i = 0;
+
+ for (i = 0; i < controller.scope.groups.length; i++) {
+ controller.scope.groups[i].selected = false;
+ }
+ controller.scope.selected_groups = [];
+
+ for (i = 0; i < controller.scope.groups.length; i++) {
+ if (controller.scope.groups[i].has_corner_selected(controller.scope.scaledX, controller.scope.scaledY)) {
+ controller.scope.clear_selections();
+ if (controller.scope.selected_groups.indexOf(controller.scope.groups[i]) === -1) {
+ controller.scope.selected_groups.push(controller.scope.groups[i]);
+ }
+ controller.scope.groups[i].selected = true;
+ controller.changeState(CornerSelected);
+ controller.scope.pressedX = controller.scope.mouseX;
+ controller.scope.pressedY = controller.scope.mouseY;
+ controller.scope.pressedScaledX = controller.scope.scaledX;
+ controller.scope.pressedScaledY = controller.scope.scaledY;
+
+ return;
+ } else if (controller.scope.groups[i].is_selected(controller.scope.scaledX, controller.scope.scaledY)) {
+ controller.scope.clear_selections();
+ if (controller.scope.selected_groups.indexOf(controller.scope.groups[i]) === -1) {
+ controller.scope.selected_groups.push(controller.scope.groups[i]);
+ }
+ controller.scope.groups[i].selected = true;
+ controller.changeState(Selected1);
+ controller.scope.pressedX = controller.scope.mouseX;
+ controller.scope.pressedY = controller.scope.mouseY;
+ controller.scope.pressedScaledX = controller.scope.scaledX;
+ controller.scope.pressedScaledY = controller.scope.scaledY;
+
+ return;
+ }
+ }
+
+ controller.next_controller.handle_message(msg_type, $event);
+
+};
+_Ready.prototype.onMouseDown.transitions = ['Selected1', 'CornerSelected'];
+
+
+_Ready.prototype.onNewGroup = function (controller) {
+ controller.scope.hide_groups = false;
+ controller.changeState(Placing);
+};
+_Ready.prototype.onNewGroup.transitions = ['Placing'];
+
+
+
+_EditLabel.prototype.start = function (controller) {
+ controller.scope.selected_groups[0].edit_label = true;
+};
+
+_EditLabel.prototype.end = function (controller) {
+ controller.scope.selected_groups[0].edit_label = false;
+};
+
+
+_EditLabel.prototype.onMouseDown = function (controller) {
+
+ controller.changeState(Ready);
+
+};
+_EditLabel.prototype.onMouseDown.transitions = ['Ready'];
+
+
+_EditLabel.prototype.onKeyDown = function (controller, msg_type, $event) {
+ //Key codes found here:
+ //https://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
+ var item = controller.scope.selected_groups[0];
+ var previous_name = item.name;
+ if ($event.keyCode === 8 || $event.keyCode === 46) { //Delete
+ item.name = item.name.slice(0, -1);
+ } else if ($event.keyCode >= 48 && $event.keyCode <=90) { //Alphanumeric
+ item.name += $event.key;
+ } else if ($event.keyCode >= 186 && $event.keyCode <=222) { //Punctuation
+ item.name += $event.key;
+ } else if ($event.keyCode === 13) { //Enter
+ controller.changeState(Selected2);
+ } else if ($event.keyCode === 32) { //Space
+ item.name += " ";
+ } else {
+ console.log($event.keyCode);
+ }
+ controller.scope.send_control_message(new messages.GroupLabelEdit(controller.scope.client_id,
+ item.id,
+ item.name,
+ previous_name));
+};
+_EditLabel.prototype.onKeyDown.transitions = ['Selected2'];
+
+_Selected2.prototype.onNewGroup = function (controller, msg_type, $event) {
+
+ controller.changeState(Ready);
+ controller.handle_message(msg_type, $event);
+
+};
+_Selected2.prototype.onNewGroup.transitions = ['Ready'];
+
+
+_Selected2.prototype.onMouseDown = function (controller, msg_type, $event) {
+
+ controller.scope.pressedX = controller.scope.mouseX;
+ controller.scope.pressedY = controller.scope.mouseY;
+ controller.scope.pressedScaledX = controller.scope.scaledX;
+ controller.scope.pressedScaledY = controller.scope.scaledY;
+
+
+
+ var groups = controller.scope.selected_groups;
+ controller.scope.selected_groups = [];
+ var i = 0;
+ for (i = 0; i < groups.length; i++) {
+ if (groups[i].has_corner_selected(controller.scope.scaledX, controller.scope.scaledY)) {
+ controller.scope.selected_groups = [];
+ break;
+ }
+ else if (groups[i].is_selected(controller.scope.scaledX, controller.scope.scaledY)) {
+ if (controller.scope.selected_groups.indexOf(groups[i]) === -1) {
+ controller.scope.selected_groups.push(groups[i]);
+ }
+ }
+ }
+
+ if (controller.scope.selected_groups.length > 0) {
+ controller.changeState(Selected3);
+ } else {
+ controller.changeState(Ready);
+ controller.handle_message(msg_type, $event);
+ }
+
+};
+_Selected2.prototype.onMouseDown.transitions = ['Ready', 'Selected3'];
+
+
+_Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
+
+ if ($event.keyCode === 8) {
+ //Delete
+ controller.changeState(Ready);
+
+ var i = 0;
+ var index = -1;
+ var groups = controller.scope.selected_groups;
+ controller.scope.selected_groups = [];
+ for (i = 0; i < groups.length; i++) {
+ index = controller.scope.groups.indexOf(groups[i]);
+ if (index !== -1) {
+ groups[i].selected = false;
+ groups[i].remote_selected = false;
+ controller.scope.groups.splice(index, 1);
+ }
+ controller.scope.send_control_message(new messages.GroupDestroy(controller.scope.client_id,
+ groups[i].id,
+ groups[i].x1,
+ groups[i].y1,
+ groups[i].x2,
+ groups[i].y2,
+ groups[i].name));
+ }
+ }
+};
+_Selected2.prototype.onKeyDown.transitions = ['Ready'];
+
+
+_Placing.prototype.onMouseDown = function (controller) {
+
+ var scope = controller.scope;
+ var group = null;
+
+ scope.pressedX = scope.mouseX;
+ scope.pressedY = scope.mouseY;
+ scope.pressedScaledX = scope.scaledX;
+ scope.pressedScaledY = scope.scaledY;
+
+ scope.clear_selections();
+
+ var id = scope.group_id_seq();
+
+ group = new models.Group(id,
+ "Group" + id,
+ scope.scaledX,
+ scope.scaledY,
+ scope.scaledX,
+ scope.scaledY,
+ false);
+ scope.send_control_message(new messages.GroupCreate(scope.client_id,
+ group.id,
+ group.x1,
+ group.y1,
+ group.x2,
+ group.y2,
+ group.name));
+
+ scope.groups.push(group);
+ scope.selected_groups.push(group);
+ group.selected = true;
+ group.selected_corner = models.BOTTOM_RIGHT;
+
+ controller.changeState(Resize);
+};
+_Placing.prototype.onMouseDown.transitions = ['CornerSelected'];
+
diff --git a/awx/network_ui/static/network_ui/src/host.directive.js b/awx/network_ui/static/network_ui/src/host.directive.js
new file mode 100644
index 0000000000..c1543430ed
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/host.directive.js
@@ -0,0 +1,5 @@
+
+function host () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/host.html' };
+}
+exports.host = host;
diff --git a/awx/network_ui/static/network_ui/src/hotkeys.fsm.js b/awx/network_ui/static/network_ui/src/hotkeys.fsm.js
new file mode 100644
index 0000000000..5ae9c78f6f
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/hotkeys.fsm.js
@@ -0,0 +1,104 @@
+var inherits = require('inherits');
+var fsm = require('./fsm.js');
+var messages = require('./messages.js');
+
+function _State () {
+}
+inherits(_State, fsm._State);
+
+
+function _Enabled () {
+ this.name = 'Enabled';
+}
+inherits(_Enabled, _State);
+var Enabled = new _Enabled();
+exports.Enabled = Enabled;
+
+function _Start () {
+ this.name = 'Start';
+}
+inherits(_Start, _State);
+var Start = new _Start();
+exports.Start = Start;
+
+function _Disabled () {
+ this.name = 'Disabled';
+}
+inherits(_Disabled, _State);
+var Disabled = new _Disabled();
+exports.Disabled = Disabled;
+
+
+
+
+_Enabled.prototype.onDisable = function (controller) {
+
+ controller.changeState(Disabled);
+
+};
+_Enabled.prototype.onDisable.transitions = ['Disabled'];
+
+
+_Enabled.prototype.onKeyDown = function(controller, msg_type, $event) {
+
+ var scope = controller.scope;
+
+ if ($event.key === 'l') {
+ scope.first_controller.handle_message("NewLink", $event);
+ return;
+ }
+
+ if ($event.key === 'd') {
+ scope.debug.hidden = !scope.debug.hidden;
+ return;
+ }
+ if ($event.key === 'p') {
+ scope.cursor.hidden = !scope.cursor.hidden;
+ return;
+ }
+ if ($event.key === 'b') {
+ scope.hide_buttons = !scope.hide_buttons;
+ return;
+ }
+ if ($event.key === 'i') {
+ scope.hide_interfaces = !scope.hide_interfaces;
+ return;
+ }
+
+ if ($event.key === 'r') {
+ scope.first_controller.handle_message("NewDevice", new messages.NewDevice("router"));
+ return;
+ }
+ else if ($event.key === 's') {
+ scope.first_controller.handle_message("NewDevice", new messages.NewDevice("switch"));
+ return;
+ }
+ else if ($event.key === 'a') {
+ scope.first_controller.handle_message("NewDevice", new messages.NewDevice("rack"));
+ return;
+ }
+ else if ($event.key === 'h') {
+ scope.first_controller.handle_message("NewDevice", new messages.NewDevice("host"));
+ return;
+ }
+
+ controller.next_controller.handle_message(msg_type, $event);
+};
+
+_Start.prototype.start = function (controller) {
+
+ controller.changeState(Enabled);
+
+};
+_Start.prototype.start.transitions = ['Enabled'];
+
+
+
+_Disabled.prototype.onEnable = function (controller) {
+
+ controller.changeState(Enabled);
+
+};
+_Disabled.prototype.onEnable.transitions = ['Enabled'];
+
+
diff --git a/awx/network_ui/static/network_ui/src/layer.directive.js b/awx/network_ui/static/network_ui/src/layer.directive.js
new file mode 100644
index 0000000000..195f598ceb
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/layer.directive.js
@@ -0,0 +1,5 @@
+
+function layer () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/layer.html' };
+}
+exports.layer = layer;
diff --git a/awx/network_ui/static/network_ui/src/link.directive.js b/awx/network_ui/static/network_ui/src/link.directive.js
new file mode 100644
index 0000000000..14c791e709
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/link.directive.js
@@ -0,0 +1,5 @@
+
+function link () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/link.html' };
+}
+exports.link = link;
diff --git a/awx/network_ui/static/network_ui/src/link.js b/awx/network_ui/static/network_ui/src/link.js
index 118fc7398e..1de49e4921 100644
--- a/awx/network_ui/static/network_ui/src/link.js
+++ b/awx/network_ui/static/network_ui/src/link.js
@@ -46,19 +46,11 @@ exports.Selecting = Selecting;
-_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
-
- if ($event.key === 'l') {
- controller.handle_message("NewLink", $event);
- }
-
- controller.next_controller.handle_message(msg_type, $event);
-};
-
-_Ready.prototype.onNewLink = function (controller) {
+_Ready.prototype.onNewLink = function (controller, msg_type, message) {
controller.scope.clear_selections();
controller.changeState(Selecting);
+ controller.next_controller.handle_message(msg_type, message);
};
@@ -90,10 +82,10 @@ _Connecting.prototype.onMouseUp = function (controller) {
if (selected_device !== null) {
controller.scope.new_link.to_device = selected_device;
i = controller.scope.new_link.to_device.interface_seq();
- to_device_interface = new models.Interface(i, "swp" + i);
+ to_device_interface = new models.Interface(i, "eth" + i);
controller.scope.new_link.to_device.interfaces.push(to_device_interface);
i = controller.scope.new_link.from_device.interface_seq();
- from_device_interface = new models.Interface(i, "swp" + i);
+ from_device_interface = new models.Interface(i, "eth" + i);
controller.scope.new_link.from_device.interfaces.push(from_device_interface);
to_device_interface.link = controller.scope.new_link;
from_device_interface.link = controller.scope.new_link;
diff --git a/awx/network_ui/static/network_ui/src/main.js b/awx/network_ui/static/network_ui/src/main.js
index c778c38c09..6a37717da7 100644
--- a/awx/network_ui/static/network_ui/src/main.js
+++ b/awx/network_ui/static/network_ui/src/main.js
@@ -1,2 +1,4 @@
-var app = require('./app.js');
-exports.app = app;
+var networkUI = require('./network.ui.app.js');
+var tower = require('./tower.app.js');
+exports.networkUI = networkUI.networkUI;
+exports.tower = tower.tower;
diff --git a/awx/network_ui/static/network_ui/src/messages.js b/awx/network_ui/static/network_ui/src/messages.js
index cbf1b55c6c..6a40b8eac5 100644
--- a/awx/network_ui/static/network_ui/src/messages.js
+++ b/awx/network_ui/static/network_ui/src/messages.js
@@ -101,13 +101,15 @@ function LinkCreate(sender, id, from_device_id, to_device_id, from_interface_id,
}
exports.LinkCreate = LinkCreate;
-function LinkDestroy(sender, id, from_id, to_id) {
+function LinkDestroy(sender, id, from_device_id, to_device_id, from_interface_id, to_interface_id, name) {
this.msg_type = "LinkDestroy";
this.id = id;
this.sender = sender;
- this.from_id = from_id;
- this.to_id = to_id;
- this.name = '';
+ this.name = name;
+ this.from_device_id = from_device_id;
+ this.to_device_id = to_device_id;
+ this.from_interface_id = from_interface_id;
+ this.to_interface_id = to_interface_id;
}
exports.LinkDestroy = LinkDestroy;
@@ -239,3 +241,79 @@ function ViewPort(sender, scale, panX, panY) {
this.panY = panY;
}
exports.ViewPort = ViewPort;
+
+function NewDevice(type) {
+ this.type = type;
+}
+exports.NewDevice = NewDevice;
+
+function GroupMove(sender, id, x1, y1, x2, y2, previous_x1, previous_y1, previous_x2, previous_y2) {
+ this.msg_type = "GroupMove";
+ this.sender = sender;
+ this.id = id;
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.previous_x1 = previous_x1;
+ this.previous_y1 = previous_y1;
+ this.previous_x2 = previous_x2;
+ this.previous_y2 = previous_y2;
+}
+exports.GroupMove = GroupMove;
+
+function GroupCreate(sender, id, x1, y1, x2, y2, name) {
+ this.msg_type = "GroupCreate";
+ this.sender = sender;
+ this.id = id;
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.name = name;
+}
+exports.GroupCreate = GroupCreate;
+
+function GroupDestroy(sender, id, previous_x1, previous_y1, previous_x2, previous_y2, previous_name, previous_type) {
+ this.msg_type = "GroupDestroy";
+ this.sender = sender;
+ this.id = id;
+ this.previous_x1 = previous_x1;
+ this.previous_y1 = previous_y1;
+ this.previous_x2 = previous_x2;
+ this.previous_y2 = previous_y2;
+ this.previous_name = previous_name;
+ this.previous_type = previous_type;
+}
+exports.GroupDestroy = GroupDestroy;
+
+function GroupLabelEdit(sender, id, name, previous_name) {
+ this.msg_type = "GroupLabelEdit";
+ this.sender = sender;
+ this.id = id;
+ this.name = name;
+ this.previous_name = previous_name;
+}
+exports.GroupLabelEdit = GroupLabelEdit;
+
+function GroupSelected(sender, id) {
+ this.msg_type = "GroupSelected";
+ this.sender = sender;
+ this.id = id;
+}
+exports.GroupSelected = GroupSelected;
+
+function GroupUnSelected(sender, id) {
+ this.msg_type = "GroupUnSelected";
+ this.sender = sender;
+ this.id = id;
+}
+exports.GroupUnSelected = GroupUnSelected;
+
+function GroupMembership(sender, id, members) {
+ this.msg_type = "GroupMembership";
+ this.sender = sender;
+ this.id = id;
+ this.members = members;
+}
+exports.GroupMembership = GroupMembership;
diff --git a/awx/network_ui/static/network_ui/src/models.js b/awx/network_ui/static/network_ui/src/models.js
index 8ea2b470a6..15b3d79b37 100644
--- a/awx/network_ui/static/network_ui/src/models.js
+++ b/awx/network_ui/static/network_ui/src/models.js
@@ -1,6 +1,7 @@
var fsm = require('./fsm.js');
var button = require('./button.js');
var util = require('./util.js');
+var inherits = require('inherits');
function Device(id, name, x, y, type) {
this.id = id;
@@ -267,6 +268,36 @@ Button.prototype.is_selected = function (x, y) {
};
+
+function ToggleButton(name, x, y, width, height, toggle_callback, untoggle_callback, default_toggled) {
+ this.name = name;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ this.callback = this.toggle;
+ this.is_pressed = default_toggled;
+ this.toggled = default_toggled;
+ this.toggle_callback = toggle_callback;
+ this.untoggle_callback = untoggle_callback;
+ this.mouse_over = false;
+ this.fsm = new fsm.FSMController(this, button.Start, null);
+}
+inherits(ToggleButton, Button);
+exports.ToggleButton = ToggleButton;
+
+ToggleButton.prototype.toggle = function () {
+ this.toggled = !this.toggled;
+ this.is_pressed = this.toggled;
+
+ if (this.toggled) {
+ this.toggle_callback();
+ } else {
+ this.untoggle_callback();
+ }
+};
+
+
function Task(id, name) {
this.id = id;
this.name = name;
@@ -276,3 +307,189 @@ function Task(id, name) {
exports.Task = Task;
Task.prototype.describeArc = util.describeArc;
+
+
+function Group(id, name, x1, y1, x2, y2, selected) {
+ this.id = id;
+ this.name = name;
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ this.selected = selected;
+ this.highlighted = false;
+ this.fsm = null;
+ this.selected_corner = null;
+ this.devices = [];
+}
+exports.Group = Group;
+
+Group.prototype.update_hightlighted = function (x, y) {
+
+ this.highlighted = this.is_highlighted(x, y);
+};
+
+Group.prototype.is_highlighted = function (x, y) {
+
+ return (x > this.left_extent() &&
+ x < this.right_extent() &&
+ y > this.top_extent() &&
+ y < this.bottom_extent());
+
+};
+
+var TOP_LEFT = 0;
+exports.TOP_LEFT = TOP_LEFT;
+var TOP_RIGHT = 1;
+exports.TOP_RIGHT = TOP_RIGHT;
+var BOTTOM_LEFT = 2;
+exports.BOTTOM_LEFT = BOTTOM_LEFT;
+var BOTTOM_RIGHT = 3;
+exports.BOTTOM_RIGHT = BOTTOM_RIGHT;
+
+Group.prototype.has_corner_selected = function (x, y) {
+
+ if (x > this.left_extent() &&
+ x < this.left_extent() + 10 &&
+ y > this.top_extent() &&
+ y < this.top_extent() + 10) {
+ return true;
+ }
+ if (x > this.left_extent() &&
+ x < this.left_extent() + 10 &&
+ y > this.bottom_extent() - 10 &&
+ y < this.bottom_extent()) {
+ return true;
+ }
+ if (x > this.right_extent() - 10 &&
+ x < this.right_extent() &&
+ y > this.bottom_extent() - 10 &&
+ y < this.bottom_extent()) {
+ return true;
+ }
+ if (x > this.right_extent() - 10 &&
+ x < this.right_extent() &&
+ y > this.top_extent() &&
+ y < this.top_extent() + 10) {
+ return true;
+ }
+
+ return false;
+};
+
+Group.prototype.select_corner = function (x, y) {
+
+ var corners = [[util.distance(this.x1, this.y1, x, y), TOP_LEFT],
+ [util.distance(this.x2, this.y2, x, y), BOTTOM_RIGHT],
+ [util.distance(this.x1, this.y2, x, y), BOTTOM_LEFT],
+ [util.distance(this.x2, this.y1, x, y), TOP_RIGHT]];
+
+ console.log(corners);
+
+ corners.sort(function(a, b) {
+ return a[0] - b[0];
+ });
+
+ if (corners[0][0] > 30) {
+ return null;
+ }
+
+ return corners[0][1];
+};
+
+Group.prototype.is_selected = function (x, y) {
+
+ if (util.pDistance(x,
+ y,
+ this.left_extent(),
+ this.top_extent(),
+ this.left_extent(),
+ this.bottom_extent()) < 10) {
+ return true;
+ }
+ if (util.pDistance(x,
+ y,
+ this.left_extent(),
+ this.top_extent(),
+ this.right_extent(),
+ this.top_extent()) < 10) {
+ return true;
+ }
+ if (util.pDistance(x,
+ y,
+ this.left_extent(),
+ this.top_extent(),
+ this.right_extent(),
+ this.top_extent()) < 40 && y > this.top_extent()) {
+ return true;
+ }
+ if (util.pDistance(x,
+ y,
+ this.right_extent(),
+ this.bottom_extent(),
+ this.right_extent(),
+ this.top_extent()) < 10) {
+ return true;
+ }
+ if (util.pDistance(x,
+ y,
+ this.right_extent(),
+ this.bottom_extent(),
+ this.left_extent(),
+ this.bottom_extent()) < 10) {
+ return true;
+ }
+
+ return false;
+};
+
+Group.prototype.width = function (scaledX) {
+ var x2 = this.x2 !== null ? this.x2 : scaledX;
+ return Math.abs(this.x1 - x2);
+};
+
+Group.prototype.height = function (scaledY) {
+ var y2 = this.y2 !== null ? this.y2 : scaledY;
+ return Math.abs(this.y1 - y2);
+};
+
+Group.prototype.top_extent = function (scaledY) {
+ var y2 = this.y2 !== null ? this.y2 : scaledY;
+ return (this.y1 < y2? this.y1 : y2);
+};
+
+Group.prototype.left_extent = function (scaledX) {
+ var x2 = this.x2 !== null ? this.x2 : scaledX;
+ return (this.x1 < x2? this.x1 : x2);
+};
+
+Group.prototype.bottom_extent = function (scaledY) {
+ var y2 = this.y2 !== null ? this.y2 : scaledY;
+ return (this.y1 > y2? this.y1 : y2);
+};
+
+Group.prototype.right_extent = function (scaledX) {
+ var x2 = this.x2 !== null ? this.x2 : scaledX;
+ return (this.x1 > x2? this.x1 : x2);
+};
+
+Group.prototype.update_membership = function (devices) {
+ var i = 0;
+ var y1 = this.top_extent();
+ var x1 = this.left_extent();
+ var y2 = this.bottom_extent();
+ var x2 = this.right_extent();
+ var old_devices = this.devices;
+ var device_ids = [];
+ this.devices = [];
+ for (i = 0; i < devices.length; i++) {
+ if (devices[i].x > x1 &&
+ devices[i].y > y1 &&
+ devices[i].x < x2 &&
+ devices[i].y < y2) {
+ this.devices.push(devices[i]);
+ device_ids.push(devices[i].id);
+ }
+ }
+ return [old_devices, this.devices, device_ids];
+};
diff --git a/awx/network_ui/static/network_ui/src/move.js b/awx/network_ui/static/network_ui/src/move.js
index ca3d4d08a7..60b813ffc7 100644
--- a/awx/network_ui/static/network_ui/src/move.js
+++ b/awx/network_ui/static/network_ui/src/move.js
@@ -59,6 +59,75 @@ inherits(_EditLabel, _State);
var EditLabel = new _EditLabel();
exports.EditLabel = EditLabel;
+
+function _Placing () {
+ this.name = 'Placing';
+}
+inherits(_Placing, _State);
+var Placing = new _Placing();
+exports.Placing = Placing;
+
+_Ready.prototype.onNewDevice = function (controller, msg_type, message) {
+
+ var scope = controller.scope;
+ var device = null;
+ var id = null;
+
+ scope.pressedX = scope.mouseX;
+ scope.pressedY = scope.mouseY;
+ scope.pressedScaledX = scope.scaledX;
+ scope.pressedScaledY = scope.scaledY;
+
+ scope.clear_selections();
+
+ if (message.type === "router") {
+ id = controller.scope.device_id_seq();
+ device = new models.Device(id,
+ "Router" + id,
+ scope.scaledX,
+ scope.scaledY,
+ "router");
+ }
+ else if (message.type === "switch") {
+ id = controller.scope.device_id_seq();
+ device = new models.Device(id,
+ "Switch" + id,
+ scope.scaledX,
+ scope.scaledY,
+ "switch");
+ }
+ else if (message.type === "rack") {
+ id = controller.scope.device_id_seq();
+ device = new models.Device(id,
+ "Rack" + id,
+ scope.scaledX,
+ scope.scaledY,
+ "rack");
+ }
+ else if (message.type === "host") {
+ id = controller.scope.device_id_seq();
+ device = new models.Device(id,
+ "Host" + id,
+ scope.scaledX,
+ scope.scaledY,
+ "host");
+ }
+
+ if (device !== null) {
+ scope.devices.push(device);
+ scope.send_control_message(new messages.DeviceCreate(scope.client_id,
+ device.id,
+ device.x,
+ device.y,
+ device.name,
+ device.type));
+ scope.selected_devices.push(device);
+ device.selected = true;
+ controller.changeState(Placing);
+ }
+};
+_Ready.prototype.onNewDevice.transitions = ['Placing'];
+
_Ready.prototype.onMouseDown = function (controller, msg_type, $event) {
var last_selected = controller.scope.select_items($event.shiftKey);
@@ -78,53 +147,6 @@ _Ready.prototype.onMouseDown.transitions = ['Selected1'];
_Ready.prototype.onTouchStart = _Ready.prototype.onMouseDown;
-_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
-
- var scope = controller.scope;
- var device = null;
-
- if ($event.key === 'r') {
- device = new models.Device(controller.scope.device_id_seq(),
- "Router",
- scope.scaledX,
- scope.scaledY,
- "router");
- }
- else if ($event.key === 's') {
- device = new models.Device(controller.scope.device_id_seq(),
- "Switch",
- scope.scaledX,
- scope.scaledY,
- "switch");
- }
- else if ($event.key === 'a') {
- device = new models.Device(controller.scope.device_id_seq(),
- "Rack",
- scope.scaledX,
- scope.scaledY,
- "rack");
- }
- else if ($event.key === 'h') {
- device = new models.Device(controller.scope.device_id_seq(),
- "Host",
- scope.scaledX,
- scope.scaledY,
- "host");
- }
-
- if (device !== null) {
- scope.devices.push(device);
- scope.send_control_message(new messages.DeviceCreate(scope.client_id,
- device.id,
- device.x,
- device.y,
- device.name,
- device.type));
- }
-
- controller.next_controller.handle_message(msg_type, $event);
-};
-
_Start.prototype.start = function (controller) {
controller.changeState(Ready);
@@ -133,6 +155,12 @@ _Start.prototype.start = function (controller) {
_Start.prototype.start.transitions = ['Ready'];
+_Selected2.prototype.onNewDevice = function (controller, msg_type, message) {
+
+ controller.changeState(Ready);
+ controller.handle_message(msg_type, message);
+};
+_Selected2.prototype.onNewDevice.transitions = ['Ready'];
_Selected2.prototype.onMouseDown = function (controller, msg_type, $event) {
@@ -182,9 +210,25 @@ _Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
var j = 0;
var index = -1;
var devices = controller.scope.selected_devices;
+ var links = controller.scope.selected_links;
var all_links = controller.scope.links.slice();
controller.scope.selected_devices = [];
controller.scope.selected_links = [];
+ for (i = 0; i < links.length; i++) {
+ index = controller.scope.links.indexOf(links[i]);
+ if (index !== -1) {
+ links[i].selected = false;
+ links[i].remote_selected = false;
+ controller.scope.links.splice(index, 1);
+ controller.scope.send_control_message(new messages.LinkDestroy(controller.scope.client_id,
+ links[i].id,
+ links[i].from_device.id,
+ links[i].to_device.id,
+ links[i].from_interface.id,
+ links[i].to_interface.id,
+ links[i].name));
+ }
+ }
for (i = 0; i < devices.length; i++) {
index = controller.scope.devices.indexOf(devices[i]);
if (index !== -1) {
@@ -207,6 +251,8 @@ _Selected2.prototype.onKeyDown = function (controller, msg_type, $event) {
}
}
}
+
+ controller.next_controller.handle_message(msg_type, $event);
};
_Selected2.prototype.onKeyDown.transitions = ['Ready'];
@@ -234,12 +280,14 @@ _Selected1.prototype.onMouseDown = util.noop;
_Move.prototype.onMouseMove = function (controller) {
var devices = controller.scope.selected_devices;
+ var groups = controller.scope.groups;
var diffX = controller.scope.scaledX - controller.scope.pressedScaledX;
var diffY = controller.scope.scaledY - controller.scope.pressedScaledY;
var i = 0;
var j = 0;
var previous_x, previous_y;
+ var membership_old_new;
for (i = 0; i < devices.length; i++) {
previous_x = devices[i].x;
previous_y = devices[i].y;
@@ -261,6 +309,15 @@ _Move.prototype.onMouseMove = function (controller) {
}
controller.scope.pressedScaledX = controller.scope.scaledX;
controller.scope.pressedScaledY = controller.scope.scaledY;
+
+
+ //TODO: Improve the performance of this code from O(n^2) to O(n) or better
+ for (i = 0; i < groups.length; i++) {
+ membership_old_new = groups[i].update_membership(controller.scope.devices);
+ controller.scope.send_control_message(new messages.GroupMembership(controller.scope.client_id,
+ groups[i].id,
+ membership_old_new[2]));
+ }
};
_Move.prototype.onTouchMove = _Move.prototype.onMouseMove;
@@ -268,13 +325,19 @@ _Move.prototype.onTouchMove = _Move.prototype.onMouseMove;
_Move.prototype.onMouseUp = function (controller, msg_type, $event) {
- controller.changeState(Selected2);
+ controller.changeState(Selected1);
controller.handle_message(msg_type, $event);
};
-_Move.prototype.onMouseUp.transitions = ['Selected2'];
+_Move.prototype.onMouseUp.transitions = ['Selected1'];
_Move.prototype.onTouchEnd = _Move.prototype.onMouseUp;
+_Move.prototype.onMouseDown = function (controller) {
+
+ controller.changeState(Selected1);
+};
+_Move.prototype.onMouseDown.transitions = ['Selected1'];
+
_Selected3.prototype.onMouseUp = function (controller) {
controller.changeState(EditLabel);
};
@@ -346,3 +409,20 @@ _EditLabel.prototype.onKeyDown = function (controller, msg_type, $event) {
}
};
_EditLabel.prototype.onKeyDown.transitions = ['Selected2'];
+
+
+_Placing.prototype.onMouseDown = function (controller) {
+
+ controller.changeState(Selected1);
+
+};
+_Placing.prototype.onMouseDown.transitions = ['Selected1'];
+
+_Placing.prototype.onMouseMove = function (controller) {
+
+ controller.changeState(Move);
+
+};
+_Placing.prototype.onMouseMove.transitions = ['Move'];
+
+
diff --git a/awx/network_ui/static/network_ui/src/network.ui.app.js b/awx/network_ui/static/network_ui/src/network.ui.app.js
new file mode 100644
index 0000000000..eda65ee413
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/network.ui.app.js
@@ -0,0 +1,46 @@
+
+//console.log = function () { };
+var angular = require('angular');
+var NetworkUIController = require('./network.ui.controller.js');
+var cursor = require('./cursor.directive.js');
+var touch = require('./touch.directive.js');
+var router = require('./router.directive.js');
+var switchd = require('./switch.directive.js');
+var host = require('./host.directive.js');
+var link = require('./link.directive.js');
+var rack = require('./rack.directive.js');
+var group = require('./group.directive.js');
+var defaultd = require('./default.directive.js');
+var quadrants = require('./quadrants.directive.js');
+var stencil = require('./stencil.directive.js');
+var layer = require('./layer.directive.js');
+var button = require('./button.directive.js');
+var statusLight = require('./status.light.directive.js');
+var taskStatus = require('./task.status.directive.js');
+var debug = require('./debug.directive.js');
+var awxNetworkUI = require('./network.ui.directive.js');
+
+var networkUI = angular.module('networkUI', [
+ 'monospaced.mousewheel',
+ 'ngTouch'
+ ])
+ .controller('NetworkUIController', NetworkUIController.NetworkUIController)
+ .directive('awxNetCursor', cursor.cursor)
+ .directive('awxNetTouch', touch.touch)
+ .directive('awxNetDebug', debug.debug)
+ .directive('awxNetRouter', router.router)
+ .directive('awxNetSwitch', switchd.switchd)
+ .directive('awxNetHost', host.host)
+ .directive('awxNetLink', link.link)
+ .directive('awxNetRack', rack.rack)
+ .directive('awxNetGroup', group.group)
+ .directive('awxNetDefault', defaultd.defaultd)
+ .directive('awxNetQuadrants', quadrants.quadrants)
+ .directive('awxNetStencil', stencil.stencil)
+ .directive('awxNetLayer', layer.layer)
+ .directive('awxNetButton', button.button)
+ .directive('awxNetStatusLight', statusLight.statusLight)
+ .directive('awxNetTaskStatus', taskStatus.taskStatus)
+ .directive('awxNetworkUi', awxNetworkUI.awxNetworkUI);
+
+exports.networkUI = networkUI;
diff --git a/awx/network_ui/static/network_ui/src/app.js b/awx/network_ui/static/network_ui/src/network.ui.controller.js
similarity index 78%
rename from awx/network_ui/static/network_ui/src/app.js
rename to awx/network_ui/static/network_ui/src/network.ui.controller.js
index 367b25be28..d320d65971 100644
--- a/awx/network_ui/static/network_ui/src/app.js
+++ b/awx/network_ui/static/network_ui/src/network.ui.controller.js
@@ -1,28 +1,41 @@
+
//console.log = function () { };
-var app = angular.module('triangular', ['monospaced.mousewheel', 'ngTouch']);
+var angular = require('angular');
var fsm = require('./fsm.js');
+var null_fsm = require('./null.fsm.js');
+var hotkeys = require('./hotkeys.fsm.js');
var view = require('./view.js');
var move = require('./move.js');
var link = require('./link.js');
+var group = require('./group.js');
var buttons = require('./buttons.js');
var time = require('./time.js');
var util = require('./util.js');
var models = require('./models.js');
var messages = require('./messages.js');
var svg_crowbar = require('../vendor/svg-crowbar.js');
+var ReconnectingWebSocket = require('reconnectingwebsocket');
-app.controller('MainCtrl', function($scope, $document, $location, $window) {
+var NetworkUIController = function($scope, $document, $location, $window) {
window.scope = $scope;
$scope.api_token = '';
+ $scope.disconnected = false;
$scope.topology_id = $location.search().topology_id || 0;
// Create a web socket to connect to the backend server
- $scope.control_socket = new window.ReconnectingWebSocket("ws://" + window.location.host + "/network_ui/topology?topology_id=" + $scope.topology_id,
+
+ if (!$scope.disconnected) {
+ $scope.control_socket = new ReconnectingWebSocket("ws://" + window.location.host + "/network_ui/topology?topology_id=" + $scope.topology_id,
null,
{debug: false, reconnectInterval: 300});
+ } else {
+ $scope.control_socket = {
+ on_message: util.noop
+ };
+ }
$scope.history = [];
$scope.client_id = 0;
$scope.onMouseDownResult = "";
@@ -48,11 +61,15 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.selected_links = [];
$scope.selected_interfaces = [];
$scope.selected_items = [];
+ $scope.selected_groups = [];
$scope.new_link = null;
- $scope.view_controller = new fsm.FSMController($scope, view.Start, null);
+ $scope.null_controller = new fsm.FSMController($scope, null_fsm.Start, null);
+ $scope.hotkeys_controller = new fsm.FSMController($scope, hotkeys.Start, $scope.null_controller);
+ $scope.view_controller = new fsm.FSMController($scope, view.Start, $scope.hotkeys_controller);
$scope.move_controller = new fsm.FSMController($scope, move.Start, $scope.view_controller);
$scope.link_controller = new fsm.FSMController($scope, link.Start, $scope.move_controller);
- $scope.buttons_controller = new fsm.FSMController($scope, buttons.Start, $scope.link_controller);
+ $scope.group_controller = new fsm.FSMController($scope, group.Start, $scope.link_controller);
+ $scope.buttons_controller = new fsm.FSMController($scope, buttons.Start, $scope.group_controller);
$scope.time_controller = new fsm.FSMController($scope, time.Start, $scope.buttons_controller);
$scope.first_controller = $scope.time_controller;
$scope.last_key = "";
@@ -62,12 +79,15 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.debug = {'hidden': true};
$scope.hide_buttons = false;
+ $scope.hide_links = false;
$scope.hide_interfaces = false;
+ $scope.hide_groups = false;
$scope.graph = {'width': window.innerWidth,
'right_column': window.innerWidth - 300,
'height': window.innerHeight};
$scope.device_id_seq = util.natural_numbers(0);
$scope.link_id_seq = util.natural_numbers(0);
+ $scope.group_id_seq = util.natural_numbers(0);
$scope.message_id_seq = util.natural_numbers(0);
$scope.time_pointer = -1;
$scope.frame = 0;
@@ -75,25 +95,10 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.replay = false;
$scope.touch_data = {};
$scope.touches = [];
-
-
- $scope.devices = [
- ];
-
- $scope.stencils = [
- //{"name": "router", "size":50, 'x':10, 'y':100},
- //{"name": "switch", "size":50, 'x':10, 'y':160},
- //{"name": "rack", "size":50, 'x':10, 'y':220},
- ];
-
- $scope.layers = [
- //{"name": "Layer 3", "size":60, 'x':window.innerWidth - 70, 'y':10},
- //{"name": "Layer 2", "size":60, 'x':window.innerWidth - 70, 'y':80},
- //{"name": "Layer 1", "size":60, 'x':window.innerWidth - 70, 'y':150},
- ];
-
- $scope.links = [
- ];
+ $scope.devices = [];
+ $scope.stencils = [];
+ $scope.links = [];
+ $scope.groups = [];
@@ -117,10 +122,12 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
var j = 0;
var devices = $scope.devices;
var links = $scope.links;
+ var groups = $scope.groups;
$scope.selected_items = [];
$scope.selected_devices = [];
$scope.selected_links = [];
$scope.selected_interfaces = [];
+ $scope.selected_groups = [];
for (i = 0; i < devices.length; i++) {
for (j = 0; j < devices[i].interfaces.length; j++) {
devices[i].interfaces[j].selected = false;
@@ -136,6 +143,9 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
}
links[i].selected = false;
}
+ for (i = 0; i < groups.length; i++) {
+ groups[i].selected = false;
+ }
};
$scope.select_items = function (multiple_selection) {
@@ -426,43 +436,96 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.onDiscoverButton = function (button) {
console.log(button.name);
var xhr = new XMLHttpRequest();
- xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/12/launch/", true);
+ xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/7/launch/", true);
xhr.onload = function () {
console.log(xhr.readyState);
};
xhr.onerror = function () {
console.error(xhr.statusText);
};
- xhr.setRequestHeader('Authorization', 'Token ' + $scope.api_token);
xhr.send();
};
$scope.onConfigureButton = function (button) {
console.log(button.name);
var xhr = new XMLHttpRequest();
- xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/11/launch/", true);
+ xhr.open("POST", "http://" + window.location.host + "/api/v1/job_templates/9/launch/", true);
xhr.onload = function () {
console.log(xhr.readyState);
};
xhr.onerror = function () {
console.error(xhr.statusText);
};
- xhr.setRequestHeader('Authorization', 'Token ' + $scope.api_token);
xhr.send();
};
+ $scope.onTogglePhysical = function () {
+ $scope.hide_links = false;
+ };
+
+ $scope.onUnTogglePhysical = function () {
+ $scope.hide_links = true;
+ };
+
+ $scope.onToggleGroup = function () {
+ $scope.hide_groups = false;
+ };
+
+ $scope.onUnToggleGroup = function () {
+ $scope.hide_groups = true;
+ $scope.group_controller.changeState(group.Ready);
+ };
+
// Buttons
$scope.buttons = [
- new models.Button("Deploy", 10, 10, 60, 50, $scope.onDeployButton),
- new models.Button("Destroy", 80, 10, 60, 50, $scope.onDestroyButton),
- new models.Button("Record", 150, 10, 60, 50, $scope.onRecordButton),
- new models.Button("Export", 220, 10, 60, 50, $scope.onExportButton),
- new models.Button("Discover", 290, 10, 80, 50, $scope.onDiscoverButton),
- new models.Button("Layout", 380, 10, 60, 50, $scope.onLayoutButton),
- new models.Button("Configure", 450, 10, 80, 50, $scope.onConfigureButton)
+ new models.Button("DEPLOY", 10, 10, 70, 30, $scope.onDeployButton),
+ new models.Button("DESTROY", 90, 10, 80, 30, $scope.onDestroyButton),
+ new models.Button("RECORD", 180, 10, 80, 30, $scope.onRecordButton),
+ new models.Button("EXPORT", 270, 10, 70, 30, $scope.onExportButton),
+ new models.Button("DISCOVER", 350, 10, 80, 30, $scope.onDiscoverButton),
+ new models.Button("LAYOUT", 440, 10, 70, 30, $scope.onLayoutButton),
+ new models.Button("CONFIGURE", 520, 10, 90, 30, $scope.onConfigureButton)
];
+ var LAYERS_X = 160;
+
+ $scope.layers = [
+ new models.ToggleButton("APPLICATION", $scope.graph.width - LAYERS_X, 10, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("PRESENTATION", $scope.graph.width - LAYERS_X, 50, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("SESSION", $scope.graph.width - LAYERS_X, 90, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("TRANSPORT", $scope.graph.width - LAYERS_X, 130, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("NETWORK", $scope.graph.width - LAYERS_X, 170, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("DATA-LINK", $scope.graph.width - LAYERS_X, 210, 120, 30, util.noop, util.noop, true),
+ new models.ToggleButton("PHYSICAL",
+ $scope.graph.width - LAYERS_X, 250, 120, 30,
+ $scope.onTogglePhysical,
+ $scope.onUnTogglePhysical,
+ true),
+ new models.ToggleButton("GROUP",
+ $scope.graph.width - LAYERS_X, 290, 120, 30,
+ $scope.onToggleGroup,
+ $scope.onUnToggleGroup,
+ true)
+ ];
+
+ var STENCIL_X = 10;
+ var STENCIL_Y = 100;
+ var STENCIL_SPACING = 40;
+
+ $scope.stencils = [
+ new models.Button("Switch", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 0, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("switch"));}),
+ new models.Button("Router", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 1, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("router"));}),
+ new models.Button("Host", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 2, 70, 30, function () {$scope.first_controller.handle_message("NewDevice", new messages.NewDevice("host"));}),
+ new models.Button("Link", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 3, 70, 30, function () { $scope.first_controller.handle_message("NewLink");}),
+ new models.Button("Group", STENCIL_X, STENCIL_Y + STENCIL_SPACING * 4, 70, 30, function () { $scope.first_controller.handle_message("NewGroup");}),
+ ];
+
+ $scope.all_buttons = [];
+ $scope.all_buttons.extend($scope.buttons);
+ $scope.all_buttons.extend($scope.layers);
+ $scope.all_buttons.extend($scope.stencils);
+
$scope.onTaskStatus = function(data) {
var i = 0;
var j = 0;
@@ -513,18 +576,42 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
var k = 0;
var device = null;
var keys = null;
+ var peers = null;
var ptm = null;
var intf = null;
for (i = 0; i < $scope.devices.length; i++) {
device = $scope.devices[i];
if (device.name === data.key) {
- keys = Object.keys(data.value.ansible_local.ptm);
- for (j = 0; j < keys.length; j++) {
- ptm = data.value.ansible_local.ptm[keys[j]];
- for (k = 0; k < device.interfaces.length; k++) {
- intf = device.interfaces[k];
- if (intf.name === ptm.port) {
- intf.link.status = ptm['cbl status'] === 'pass';
+
+ //Check PTM
+ if (data.value.ansible_local !== undefined &&
+ data.value.ansible_local.ptm !== undefined) {
+ keys = Object.keys(data.value.ansible_local.ptm);
+ for (j = 0; j < keys.length; j++) {
+ ptm = data.value.ansible_local.ptm[keys[j]];
+ for (k = 0; k < device.interfaces.length; k++) {
+ intf = device.interfaces[k];
+ if (intf.name === ptm.port) {
+ intf.link.status = ptm['cbl status'] === 'pass';
+ }
+ }
+ }
+ }
+
+ //Check LLDP
+ if (data.value.ansible_net_neighbors !== undefined) {
+ keys = Object.keys(data.value.ansible_net_neighbors);
+ for (j = 0; j < keys.length; j++) {
+ peers = data.value.ansible_net_neighbors[keys[j]];
+ for (k = 0; k < peers.length; k++) {
+ intf = $scope.getDeviceInterface(device.name, keys[j]);
+ if (intf !== null && intf.link !== null) {
+ if (intf.link.to_interface === intf) {
+ intf.link.status = ($scope.getDeviceInterface(peers[k].host, peers[k].port) === intf.link.from_interface);
+ } else {
+ intf.link.status = ($scope.getDeviceInterface(peers[k].host, peers[k].port) === intf.link.to_interface);
+ }
+ }
}
}
}
@@ -534,6 +621,34 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.$apply();
};
+ $scope.getDevice = function(name) {
+
+ var i = 0;
+ for (i = 0; i < $scope.devices.length; i++) {
+ if ($scope.devices[i].name === name) {
+ return $scope.devices[i];
+ }
+ }
+
+ return null;
+ };
+
+ $scope.getDeviceInterface = function(device_name, interface_name) {
+
+ var i = 0;
+ var k = 0;
+ for (i = 0; i < $scope.devices.length; i++) {
+ if ($scope.devices[i].name === device_name) {
+ for (k = 0; k < $scope.devices[i].interfaces.length; k++) {
+ if ($scope.devices[i].interfaces[k].name === interface_name) {
+ return $scope.devices[i].interfaces[k];
+ }
+ }
+ }
+ }
+ return null;
+ };
+
$scope.onDeviceCreate = function(data) {
$scope.create_device(data);
};
@@ -583,6 +698,16 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
}
};
+ $scope.forGroup = function(group_id, data, fn) {
+ var i = 0;
+ for (i = 0; i < $scope.groups.length; i++) {
+ if ($scope.groups[i].id === group_id) {
+ fn($scope.groups[i], data);
+ break;
+ }
+ }
+ };
+
$scope.onDeviceLabelEdit = function(data) {
$scope.edit_device_label(data);
};
@@ -674,7 +799,7 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
link = $scope.links[i];
if (link.id === data.id &&
link.from_device.id === data.from_device_id &&
- link.to_device.id === data.to_device_id &&
+ link.to_device.id === data.to_device_id &&
link.to_interface.id === data.to_interface_id &&
link.from_interface.id === data.from_interface_id) {
link.from_interface.link = null;
@@ -751,6 +876,17 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
}
};
+
+ $scope.onGroupLabelEdit = function(data) {
+ $scope.edit_group_label(data);
+ };
+
+ $scope.edit_group_label = function(data) {
+ $scope.forGroup(data.id, data, function(group, data) {
+ group.name = data.name;
+ });
+ };
+
$scope.redo = function(type_data) {
var type = type_data[0];
var data = type_data[1];
@@ -831,6 +967,9 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.panX = data.panX;
$scope.panY = data.panX;
$scope.current_scale = data.scale;
+ $scope.link_id_seq = util.natural_numbers(data.link_id_seq);
+ $scope.group_id_seq = util.natural_numbers(data.group_id_seq);
+ $scope.device_id_seq = util.natural_numbers(data.device_id_seq);
$location.search({topology_id: data.topology_id});
};
@@ -863,7 +1002,6 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.onSnapshot = function (data) {
-
//Erase the existing state
$scope.devices = [];
$scope.links = [];
@@ -878,11 +1016,13 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
var new_intf = null;
var max_device_id = null;
var max_link_id = null;
+ var max_group_id = null;
var min_x = null;
var min_y = null;
var max_x = null;
var max_y = null;
var new_link = null;
+ var new_group = null;
//Build the devices
for (i = 0; i < data.devices.length; i++) {
@@ -907,6 +1047,7 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
device.x,
device.y,
device.type);
+ new_device.interface_seq = util.natural_numbers(device.interface_id_seq);
$scope.devices.push(new_device);
device_map[device.id] = new_device;
device_interface_map[device.id] = {};
@@ -938,6 +1079,28 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
device_interface_map[link.to_device_id][link.to_interface_id].link = new_link;
}
+ //Build the groups
+ var group = null;
+ for (i = 0; i < data.groups.length; i++) {
+ group = data.groups[i];
+ if (max_group_id === null || group.id > max_group_id) {
+ max_group_id = group.id;
+ }
+ new_group = new models.Group(group.id,
+ group.name,
+ group.x1,
+ group.y1,
+ group.x2,
+ group.y2,
+ false);
+ if (group.members !== undefined) {
+ for (j=0; j < group.members.length; j++) {
+ new_group.devices.push(device_map[group.members[j]]);
+ }
+ }
+ $scope.groups.push(new_group);
+ }
+
var diff_x;
var diff_y;
@@ -969,6 +1132,10 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
if (max_link_id !== null) {
$scope.link_id_seq = util.natural_numbers(max_link_id);
}
+ //Update the group_id_seq to be greater than all group ids to prevent duplicate ids.
+ if (max_group_id !== null) {
+ $scope.group_id_seq = util.natural_numbers(max_group_id);
+ }
$scope.updateInterfaceDots();
};
@@ -1034,7 +1201,11 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
}
}
var data = messages.serialize(message);
- $scope.control_socket.send(data);
+ if (!$scope.disconnected) {
+ $scope.control_socket.send(data);
+ } else {
+ console.log(data);
+ }
};
@@ -1047,6 +1218,8 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.graph.right_column = $window.innerWidth - 300;
$scope.graph.height = $window.innerHeight;
+ $scope.update_size();
+
// manuall $digest required as resize event
// is outside of angular
$scope.$digest();
@@ -1057,68 +1230,21 @@ app.controller('MainCtrl', function($scope, $document, $location, $window) {
$scope.frame = Math.floor(window.performance.now());
$scope.$apply();
}, 17);
-});
-app.directive('cursor', function() {
- return { restrict: 'A', templateUrl: 'widgets/cursor.html' };
-});
+ console.log("Network UI started");
-app.directive('touch', function() {
- return { restrict: 'A', templateUrl: 'widgets/touch.html' };
-});
+ $scope.$on('$destroy', function () {
+ console.log("Network UI stopping");
+ $document.unbind('keydown', $scope.onKeyDown);
+ });
-app.directive('debug', function() {
- return { restrict: 'A', templateUrl: 'widgets/debug.html' };
-});
+ $scope.update_size = function () {
+ var i = 0;
+ for (i = 0; i < $scope.layers.length; i++) {
+ $scope.layers[i].x = $scope.graph.width - 140;
+ }
+ };
+};
-app.directive('router', function() {
- return { restrict: 'A', templateUrl: 'widgets/router.html' };
-});
-
-app.directive('switch', function() {
- return { restrict: 'A', templateUrl: 'widgets/switch.html' };
-});
-
-app.directive('host', function() {
- return { restrict: 'A', templateUrl: 'widgets/host.html' };
-});
-
-app.directive('link', function() {
- return { restrict: 'A', templateUrl: 'widgets/link.html' };
-});
-
-app.directive('rack', function() {
- return { restrict: 'A', templateUrl: 'widgets/rack.html' };
-});
-
-app.directive('default', function() {
- return { restrict: 'A', templateUrl: 'widgets/default.html' };
-});
-
-app.directive('quadrants', function() {
- return { restrict: 'A', templateUrl: 'widgets/quadrants.html' };
-});
-
-app.directive('stencil', function() {
- return { restrict: 'A', templateUrl: 'widgets/stencil.html' };
-});
-
-app.directive('layer', function() {
- return { restrict: 'A', templateUrl: 'widgets/layer.html' };
-});
-
-app.directive('button', function() {
- return { restrict: 'A', templateUrl: 'widgets/button.html' };
-});
-
-app.directive('statusLight', function() {
- return { restrict: 'A', templateUrl: 'widgets/status_light.html' };
-});
-
-app.directive('taskStatus', function() {
- return { restrict: 'A', templateUrl: 'widgets/task_status.html' };
-});
-
-
-
-exports.app = app;
+exports.NetworkUIController = NetworkUIController;
+console.log("Network UI loaded");
diff --git a/awx/network_ui/static/network_ui/src/network.ui.directive.js b/awx/network_ui/static/network_ui/src/network.ui.directive.js
new file mode 100644
index 0000000000..e225ccb5f9
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/network.ui.directive.js
@@ -0,0 +1,5 @@
+
+function awxNetworkUI () {
+ return { restrict: 'E', templateUrl: '/static/network_ui/widgets/network_ui.html' };
+}
+exports.awxNetworkUI = awxNetworkUI;
diff --git a/awx/network_ui/static/network_ui/src/null.fsm.js b/awx/network_ui/static/network_ui/src/null.fsm.js
new file mode 100644
index 0000000000..887e811b80
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/null.fsm.js
@@ -0,0 +1,35 @@
+var inherits = require('inherits');
+var fsm = require('./fsm.js');
+
+function _State () {
+}
+inherits(_State, fsm._State);
+
+
+function _Start () {
+ this.name = 'Start';
+}
+inherits(_Start, _State);
+var Start = new _Start();
+exports.Start = Start;
+
+function _Ready () {
+ this.name = 'Ready';
+}
+inherits(_Ready, _State);
+var Ready = new _Ready();
+exports.Ready = Ready;
+
+
+
+
+_Start.prototype.start = function (controller) {
+
+ controller.changeState(Ready);
+
+};
+_Start.prototype.start.transitions = ['Ready'];
+
+
+
+
diff --git a/awx/network_ui/static/network_ui/src/quadrants.directive.js b/awx/network_ui/static/network_ui/src/quadrants.directive.js
new file mode 100644
index 0000000000..60a1ae4519
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/quadrants.directive.js
@@ -0,0 +1,5 @@
+
+function quadrants () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/quadrants.html' };
+}
+exports.quadrants = quadrants;
diff --git a/awx/network_ui/static/network_ui/src/rack.directive.js b/awx/network_ui/static/network_ui/src/rack.directive.js
new file mode 100644
index 0000000000..45612f9b8d
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/rack.directive.js
@@ -0,0 +1,5 @@
+
+function rack () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/rack.html' };
+}
+exports.rack = rack;
diff --git a/awx/network_ui/static/network_ui/src/router.directive.js b/awx/network_ui/static/network_ui/src/router.directive.js
new file mode 100644
index 0000000000..753090f82b
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/router.directive.js
@@ -0,0 +1,5 @@
+
+function router () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/router.html' };
+}
+exports.router = router;
diff --git a/awx/network_ui/static/network_ui/src/status.light.directive.js b/awx/network_ui/static/network_ui/src/status.light.directive.js
new file mode 100644
index 0000000000..e2ee6eec45
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/status.light.directive.js
@@ -0,0 +1,5 @@
+
+function statusLight () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/status_light.html' };
+}
+exports.statusLight = statusLight;
diff --git a/awx/network_ui/static/network_ui/src/stencil.directive.js b/awx/network_ui/static/network_ui/src/stencil.directive.js
new file mode 100644
index 0000000000..d6c7a42af3
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/stencil.directive.js
@@ -0,0 +1,5 @@
+
+function stencil () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/stencil.html' };
+}
+exports.stencil = stencil;
diff --git a/awx/network_ui/static/network_ui/src/style.less b/awx/network_ui/static/network_ui/src/style.less
index e69693957b..ff11fd4bc3 100644
--- a/awx/network_ui/static/network_ui/src/style.less
+++ b/awx/network_ui/static/network_ui/src/style.less
@@ -1,4 +1,17 @@
-/* Put your css in here */
+
+@font-face {
+ font-family: 'Open Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: url(/static/assets/OpenSans-Regular.ttf);
+}
+
+@font-face {
+ font-family: 'Open Sans';
+ font-style: bold;
+ font-weight: 600;
+ src: url(/static/assets/OpenSans-Bold.ttf);
+}
@selected-red: #c9232c;
@selected-mango: #ff5850;
@@ -8,308 +21,469 @@
@dark-widget-detail: #707070;
@widget-body: #D7D7D7;
@link: #D7D7D7;
+@group: #707070;
@debug-copynot: rgb(77,200,242);
-@button-body: #f6f6f6;
+@button-body: #ffffff;
@button-text: #707070;
-@button-outline: #b4b6b4;
-@button-body-hover: #dfdfdf;
-@button-body-pressed: #d5d5d5;
+@button-outline: #b7b7b7;
+@button-body-hover: #f2f2f2;
+@button-body-pressed: #848992;
+@button-text-pressed: #ffffff;
@green: #5CB85C;
@red: #D9534F;
-html {
- overflow: hidden;
-}
-
-body {
- background-color: yellow;
- padding: 0;
- margin: 0;
- height: 100%;
- width: 100%;
- overflow: hidden;
-}
-
-svg {
+.NetworkUI {
background-color: @light-background;
cursor: none;
}
-svg text {
+.NetworkUI__text {
fill: @button-text;
+ font-family: 'Open Sans';
}
-.debug text {
+.NetworkUI__debug-text {
fill: @debug-copynot;
+ font-family: 'Open Sans';
}
-
-line.selected {
- stroke: @selected-blue;
- stroke-width: 6;
-}
-
-line.remote-selected {
- stroke: @selected-mango;
- stroke-width: 6;
-}
-
-line.selected-conflict {
- stroke: @selected-red;
- stroke-width: 6;
-}
-
-svg rect.debug {
+.NetworkUI--debug {
fill-opacity: 0;
stroke: @debug-copynot;
stroke-width: 1;
}
-svg line.link {
+
+.NetworkUI__link--selected {
+ stroke: @selected-blue;
+ stroke-width: 6;
+}
+
+.NetworkUI__link--remote-selected {
+ stroke: @selected-mango;
+ stroke-width: 6;
+}
+
+.NetworkUI__link--selected-conflict {
+ stroke: @selected-red;
+ stroke-width: 6;
+}
+
+
+.NetworkUI__link {
stroke: @link;
stroke-width: 2;
}
-svg line.link-pass {
+.NetworkUI__link--link-pass {
stroke: @green;
stroke-width: 2;
}
-svg line.link-fail {
+.NetworkUI__link--link-fail {
stroke: @red;
stroke-width: 2;
}
-svg line.cursor {
- stroke: @dark-widget-detail;
- stroke-width: 2;
-}
-
-svg line.debug {
+.NetworkUI__link--debug {
stroke: @debug-copynot;
stroke-width: 1;
}
-.debug-cursor line {
+.NetworkUI__cursor {
+ stroke: @dark-widget-detail;
+ stroke-width: 2;
+}
+
+.NetworkUI__debug-cursor {
stroke: @debug-copynot;
stroke-width: 4;
}
-.hidden {
+.NetworkUI--hidden {
display: none;
}
-.router circle {
+.NetworkUI__router {
fill: @widget-body;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-.router circle.selected {
+.NetworkUI__router--selected {
stroke: @selected-blue;
stroke-width: 4;
}
-.router circle.remote-selected {
+.NetworkUI__router--remote-selected {
stroke: @selected-mango;
stroke-width: 4;
}
-.router circle.selected-conflict {
+.NetworkUI__router--selected-conflict {
stroke: @selected-red;
stroke-width: 4;
}
-.router line {
+.NetworkUI__router line {
stroke: @light-widget-detail;
stroke-width: 20;
}
-.router polygon {
+.NetworkUI__router polygon {
fill: @light-widget-detail;
+ stroke: none;
}
-.switch rect {
+.NetworkUI__router-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+}
+
+
+.NetworkUI__router-text--selected {
+ font-family: 'Open Sans';
+}
+
+
+.NetworkUI__switch {
fill: @widget-body;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-.switch rect.selected {
+.NetworkUI__switch--selected {
stroke: @selected-blue;
stroke-width: 10;
}
-.switch rect.remote-selected {
+.NetworkUI__switch--remote-selected {
stroke: @selected-mango;
stroke-width: 10;
}
-.switch rect.selected-conflict {
+.NetworkUI__switch--selected-conflict {
stroke: @selected-red;
stroke-width: 10;
}
-.switch line {
+.NetworkUI__switch line {
stroke: @light-widget-detail;
stroke-width: 20;
}
-.switch polygon {
+.NetworkUI__switch polygon {
fill: @light-widget-detail;
+ stroke: none;
}
-.rack rect {
+.NetworkUI__switch-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+}
+
+
+.NetworkUI__switch-text--selected {
+ font-family: 'Open Sans';
+}
+
+.NetworkUI__rack {
fill: @widget-body;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-.rack rect.background {
+.NetworkUI__rack-background {
fill: @light-background;
stroke: @light-background;
stroke-width: 2;
}
-.rack rect.selected {
+.NetworkUI__rack--selected {
fill: @selected-blue;
stroke: @selected-blue;
stroke-width: 10;
}
-.rack rect.remote-selected {
+.NetworkUI__rack--remote-selected {
fill: @selected-mango;
stroke: @selected-mango;
stroke-width: 10;
}
-.rack rect.selected-conflict {
+.NetworkUI__rack--selected-conflict {
fill: @selected-red;
stroke: @selected-red;
stroke-width: 10;
}
-.rack line {
+.NetworkUI__rack line {
stroke: @light-widget-detail;
stroke-width: 20;
}
-.rack circle {
+.NetworkUI__rack circle {
fill: @light-widget-detail;
+ stroke: none;
}
-.button rect {
+.NetworkUI__button {
fill: @button-body;
stroke: @button-outline;
stroke-width: 1;
}
-.button text {
+.NetworkUI__button-text {
fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
}
-.button-pressed rect {
+.NetworkUI__button--button-pressed {
fill: @button-body-pressed;
stroke: @button-outline;
stroke-width: 1;
}
-.button-pressed text {
- fill: @button-text;
+.NetworkUI__button-text--button-pressed {
+ fill: @button-text-pressed;
+ font-family: 'Open Sans';
+ font-size: 14px;
}
-.button-hover rect {
+.NetworkUI__button--button-hover {
fill: @button-body-hover;
stroke: @button-outline;
stroke-width: 1;
}
-.button-hover text {
+.NetworkUI__button-text--button-hover {
fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
}
-.host rect {
+.NetworkUI__layer {
+ fill: @button-body;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__layer-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__layer--button-pressed {
+ fill: @button-body-pressed;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__layer-text--button-pressed {
+ fill: @button-text-pressed;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__layer--button-hover {
+ fill: @button-body-hover;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__layer-text--button-hover {
+ fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__stencil {
+ fill: @button-body;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__stencil-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__stencil--button-pressed {
+ fill: @button-body-pressed;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__stencil-text--button-pressed {
+ fill: @button-text-pressed;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__stencil--button-hover {
+ fill: @button-body-hover;
+ stroke: @button-outline;
+ stroke-width: 1;
+}
+
+.NetworkUI__stencil-text--button-hover {
+ fill: @button-text;
+ font-family: 'Open Sans';
+ font-size: 14px;
+}
+
+.NetworkUI__host {
fill: @widget-body;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-.host rect.background {
+.NetworkUI__host-background {
fill: @light-background;
stroke: @light-background;
stroke-width: 2;
}
-.host rect.selected {
+.NetworkUI__host--selected {
fill: @selected-blue;
stroke: @selected-blue;
stroke-width: 10;
}
-.host rect.remote-selected {
+.NetworkUI__host--remote-selected {
fill: @selected-mango;
stroke: @selected-mango;
stroke-width: 10;
}
-.host rect.selected-conflict {
+.NetworkUI__host--selected-conflict {
fill: @selected-red;
stroke: @selected-red;
stroke-width: 10;
}
-.host line {
+.NetworkUI__host line {
stroke: @light-widget-detail;
stroke-width: 20;
}
-.host circle {
+.NetworkUI__host circle {
fill: @light-widget-detail;
+ stroke: none;
}
-circle.status {
+
+.NetworkUI__host-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+}
+
+
+.NetworkUI__host-text--selected {
+ font-family: 'Open Sans';
+}
+
+.NetworkUI__status {
fill: @widget-body;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-circle.pass {
+.NetworkUI__status--pass {
fill: @green;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-circle.fail {
+.NetworkUI__status--fail {
fill: @red;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-path.status {
+.NetworkUI__status-path {
fill: none;
stroke: @dark-widget-detail;
stroke-width: 2;
}
-circle.debug {
+.NetworkUI__circle-debug {
fill: @debug-copynot;
}
-circle.interface {
+.NetworkUI__interface {
fill: @dark-widget-detail;
}
-circle.selected {
+.NetworkUI__interface--selected {
fill: @selected-blue;
}
-text.interface {
+.NetworkUI__interface-text {
+ fill: @button-text;
font-size: 8px;
+ font-family: 'Open Sans';
}
-.touch circle {
+
+.NetworkUI__interface-text--selected {
+ font-size: 8px;
+ font-family: 'Open Sans';
+}
+
+.NetworkUI__link-text {
+ fill: @button-text;
+ font-size: 8px;
+ font-family: 'Open Sans';
+}
+
+.NetworkUI__touch {
stroke: @debug-copynot;
fill: none;
}
+
+
+.NetworkUI__group--selected {
+ stroke: @selected-blue;
+ stroke-width: 6;
+ fill: none;
+}
+
+.NetworkUI__group--remote-selected {
+ stroke: @selected-mango;
+ stroke-width: 6;
+ fill: none;
+}
+
+.NetworkUI__group--selected-conflict {
+ stroke: @selected-red;
+ stroke-width: 6;
+ fill: none;
+}
+
+.NetworkUI__group {
+ stroke: @group;
+ stroke-width: 2;
+ fill: none;
+}
+
+.NetworkUI__group--debug {
+ stroke: @debug-copynot;
+ stroke-width: 1;
+ fill: none;
+}
+
+.NetworkUI__group-text {
+ fill: @button-text;
+ font-family: 'Open Sans';
+}
+
+
+.NetworkUI__group-text--selected {
+ font-family: 'Open Sans';
+}
diff --git a/awx/network_ui/static/network_ui/src/switch.directive.js b/awx/network_ui/static/network_ui/src/switch.directive.js
new file mode 100644
index 0000000000..7d123e1b9d
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/switch.directive.js
@@ -0,0 +1,5 @@
+
+function switchd () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/switch.html' };
+}
+exports.switchd = switchd;
diff --git a/awx/network_ui/static/network_ui/src/task.status.directive.js b/awx/network_ui/static/network_ui/src/task.status.directive.js
new file mode 100644
index 0000000000..31db3fb435
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/task.status.directive.js
@@ -0,0 +1,5 @@
+
+function taskStatus () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/task_status.html' };
+}
+exports.taskStatus = taskStatus;
diff --git a/awx/network_ui/static/network_ui/src/time.js b/awx/network_ui/static/network_ui/src/time.js
index ce8b61d237..6b731acefc 100644
--- a/awx/network_ui/static/network_ui/src/time.js
+++ b/awx/network_ui/static/network_ui/src/time.js
@@ -45,6 +45,7 @@ _Past.prototype.onMessage = function(controller, msg_type, message) {
'DeviceDestroy',
'DeviceMove',
'DeviceLabelEdit',
+ 'GroupLabelEdit',
'LinkLabelEdit',
'InterfaceLabelEdit',
'InterfaceCreate',
@@ -98,6 +99,7 @@ _Past.prototype.onRedo = function(controller, msg_type, message) {
}
}
};
+_Past.prototype.onRedo.transitions = ['Present'];
_Past.prototype.onCoverageRequest = function(controller) {
controller.scope.send_coverage();
@@ -195,7 +197,7 @@ _Past.prototype.onMouseWheel = function (controller, msg_type, message) {
}
};
-_Past.prototype.onMouseWheel.transitions = ['Past'];
+_Past.prototype.onMouseWheel.transitions = ['Present'];
_Past.prototype.onKeyDown = function(controller, msg_type, $event) {
@@ -217,7 +219,7 @@ _Past.prototype.onKeyDown = function(controller, msg_type, $event) {
controller.next_controller.handle_message(msg_type, $event);
}
};
-_Past.prototype.onKeyDown.transitions = ['Past'];
+_Past.prototype.onKeyDown.transitions = ['Present'];
_Past.prototype.undo = function(controller) {
@@ -271,18 +273,18 @@ _Present.prototype.onMessage = function(controller, msg_type, message) {
'DeviceDestroy',
'DeviceMove',
'DeviceLabelEdit',
- 'LinkLabelEdit',
- 'InterfaceLabelEdit',
+ 'GroupLabelEdit',
'InterfaceCreate',
+ 'InterfaceLabelEdit',
'LinkCreate',
'LinkDestroy',
+ 'LinkLabelEdit',
'Snapshot'].indexOf(type) !== -1) {
controller.scope.history.push(message.data);
}
controller.handle_message(type, data);
};
-_Present.prototype.onMessage.transitions = ['Past'];
_Present.prototype.onMultipleMessage = function(controller, msg_type, message) {
@@ -342,6 +344,11 @@ _Present.prototype.onDeviceLabelEdit = function(controller, msg_type, message) {
controller.scope.onDeviceLabelEdit(message);
}
};
+_Present.prototype.onGroupLabelEdit = function(controller, msg_type, message) {
+ if (message.sender !== controller.scope.client_id) {
+ controller.scope.onGroupLabelEdit(message);
+ }
+};
_Present.prototype.onLinkLabelEdit = function(controller, msg_type, message) {
if (message.sender !== controller.scope.client_id) {
controller.scope.onLinkLabelEdit(message);
@@ -369,6 +376,7 @@ _Present.prototype.onUndo = function(controller, msg_type, message) {
controller.changeState(Past);
}
};
+_Present.prototype.onUndo.transitions = ['Past'];
_Present.prototype.onSnapshot = function(controller, msg_type, message) {
if (message.sender !== controller.scope.client_id) {
controller.scope.onSnapshot(message);
@@ -471,7 +479,6 @@ _Present.prototype.onMouseWheelEvent = function(controller, msg_type, message) {
controller.scope.onKeyDown(message);
}
};
-_Present.prototype.onMessage.transitions = ['Past'];
_Present.prototype.onMouseWheel = function (controller, msg_type, message) {
diff --git a/awx/network_ui/static/network_ui/src/touch.directive.js b/awx/network_ui/static/network_ui/src/touch.directive.js
new file mode 100644
index 0000000000..6c373fee32
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/touch.directive.js
@@ -0,0 +1,5 @@
+
+function touch () {
+ return { restrict: 'A', templateUrl: '/static/network_ui/widgets/touch.html' };
+}
+exports.touch = touch;
diff --git a/awx/network_ui/static/network_ui/src/tower.app.js b/awx/network_ui/static/network_ui/src/tower.app.js
new file mode 100644
index 0000000000..44890e9805
--- /dev/null
+++ b/awx/network_ui/static/network_ui/src/tower.app.js
@@ -0,0 +1,28 @@
+
+var angular = require('angular');
+var ui_router = require('angular-ui-router');
+
+var tower = angular.module('tower', ['networkUI', 'ui.router']);
+
+tower.config(function($stateProvider, $urlRouterProvider) {
+
+ $urlRouterProvider.otherwise('/index');
+
+ $stateProvider
+ .state({
+ name: 'index',
+ url: '/index',
+ template: 'Topology'
+ });
+
+ $stateProvider
+ .state({
+ name: 'topology',
+ url: '/topology',
+ template: ""
+ });
+});
+
+exports.tower = tower;
+exports.ui_router = ui_router;
+
diff --git a/awx/network_ui/static/network_ui/src/util.js b/awx/network_ui/static/network_ui/src/util.js
index a0db4d38a6..abd8d78902 100644
--- a/awx/network_ui/static/network_ui/src/util.js
+++ b/awx/network_ui/static/network_ui/src/util.js
@@ -1,3 +1,12 @@
+Array.prototype.extend = function (other_array) {
+ /* you should include a test to check whether other_array really is an array */
+ var i = 0;
+ for (i = 0; i < other_array.length; i++) {
+ this.push(other_array[i]);
+ }
+};
+
+
var math = require('mathjs');
function noop () {
@@ -10,6 +19,13 @@ function natural_numbers (start) {
}
exports.natural_numbers = natural_numbers;
+
+function distance (x1, y1, x2, y2) {
+
+ return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
+}
+exports.distance = distance;
+
// polarToCartesian
// @wdebeaum, @opsb
// from http://stackoverflow.com/questions/5736398/how-to-calculate-the-svg-path-for-an-arc-of-a-circle
diff --git a/awx/network_ui/static/network_ui/src/view.js b/awx/network_ui/static/network_ui/src/view.js
index 4b3497be40..4f55d57ade 100644
--- a/awx/network_ui/static/network_ui/src/view.js
+++ b/awx/network_ui/static/network_ui/src/view.js
@@ -86,28 +86,6 @@ _Ready.prototype.onMouseWheel = function (controller, msg_type, $event) {
controller.handle_message(msg_type, $event);
};
-_Ready.prototype.onKeyDown = function(controller, msg_type, $event) {
-
- var scope = controller.scope;
-
- if ($event.key === 'd') {
- scope.debug.hidden = !scope.debug.hidden;
- return;
- }
- if ($event.key === 'p') {
- scope.cursor.hidden = !scope.cursor.hidden;
- return;
- }
- if ($event.key === 'b') {
- scope.hide_buttons = !scope.hide_buttons;
- return;
- }
- if ($event.key === 'i') {
- scope.hide_interfaces = !scope.hide_interfaces;
- return;
- }
-};
-
_Start.prototype.start = function (controller) {
@@ -127,6 +105,7 @@ _Scale.prototype.onMouseWheel = function (controller, msg_type, message) {
controller.scope.updatePanAndScale();
controller.changeState(Ready);
};
+_Scale.prototype.onMouseWheel.transitions = ['Ready'];
_Pressed.prototype.onMouseUp = function (controller) {
@@ -134,6 +113,7 @@ _Pressed.prototype.onMouseUp = function (controller) {
controller.changeState(Ready);
};
+_Pressed.prototype.onMouseUp.transitions = ['Ready'];
_Pressed.prototype.onTouchEnd = _Pressed.prototype.onMouseUp;
@@ -142,6 +122,7 @@ _Pressed.prototype.onMouseMove = function (controller, msg_type, $event) {
controller.changeState(Pan);
controller.handle_message(msg_type, $event);
};
+_Pressed.prototype.onMouseMove.transitions = ['Pan'];
_Pressed.prototype.onTouchMove = _Pressed.prototype.onMouseMove;
@@ -186,5 +167,6 @@ _Pan.prototype.onMouseUp = function (controller) {
controller.changeState(Ready);
};
+_Pan.prototype.onMouseUp.transitions = ['Ready'];
_Pan.prototype.onTouchEnd = _Pan.prototype.onMouseUp;
diff --git a/awx/network_ui/static/network_ui/templates/fsm.jst b/awx/network_ui/static/network_ui/templates/fsm.jst
index ea3378933f..afe75f5cd8 100644
--- a/awx/network_ui/static/network_ui/templates/fsm.jst
+++ b/awx/network_ui/static/network_ui/templates/fsm.jst
@@ -5,22 +5,22 @@ function _State () {
}
inherits(_State, fsm._State);
-{%for state, functions in states%}
-function _{{state}} () {
- this.name = '{{state}}';
+{%for state in states%}
+function _{{state.label}} () {
+ this.name = '{{state.label}}';
}
-inherits(_{{state}}, _State);
-var {{state}} = new _{{state}}();
-exports.{{state}} = {{state}};
+inherits(_{{state.label}}, _State);
+var {{state.label}} = new _{{state.label}}();
+exports.{{state.label}} = {{state.label}};
{%endfor%}
-{%for state, functions in states%}
-{%for fn, transitions in functions%}
-_{{state}}.prototype.{{fn}} = function (controller) {
+{%for state in states%}
+{%for fn, transitions in state.functions%}
+_{{state.label}}.prototype.{{fn}} = function (controller) {
{%for tn in transitions %}
controller.changeState({{tn.to_state}});
{%endfor%}
};
-_{{state}}.prototype.{{fn}}.transitions = [{%for t in transitions%}'{{t.to_state}}'{% if not loop.last%}, {%endif%}{%endfor%}];
+_{{state.label}}.prototype.{{fn}}.transitions = [{%for t in transitions%}'{{t.to_state}}'{% if not loop.last%}, {%endif%}{%endfor%}];
{%endfor%}
{%endfor%}
diff --git a/awx/network_ui/static/network_ui/tools/transform_fsm.py b/awx/network_ui/static/network_ui/tools/transform_fsm.py
new file mode 100755
index 0000000000..21da6e74ce
--- /dev/null
+++ b/awx/network_ui/static/network_ui/tools/transform_fsm.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Usage:
+ transform_fsm [options]