From 38eb2691a823bde10009f94a18bd5bfc4a912252 Mon Sep 17 00:00:00 2001 From: Ben Thomasson Date: Thu, 22 Mar 2018 19:00:32 -0400 Subject: [PATCH] Updates models based on PR feedback from matburt et al. * Moves topology_data to views * Changes id to cid * Changes pk to id * Changes host_id and inventory_id to ForeignKeys * Resets migrations for network_ui * Cleans up old files --- awx/network_ui/consumers.py | 106 +++++++++++------- awx/network_ui/{designs => docs}/README.md | 0 awx/network_ui/{designs => docs}/messages.yml | 0 awx/network_ui/{designs => docs}/models.png | Bin awx/network_ui/{designs => docs}/models.yml | 0 awx/network_ui/migrations/0001_initial.py | 25 +++-- awx/network_ui/models.py | 22 ++-- awx/network_ui/serializers.py | 69 ------------ awx/network_ui/static/network_ui/.gitignore | 8 -- awx/network_ui/static/network_ui/.jshintrc | 47 -------- awx/network_ui/tools/Makefile | 13 --- awx/network_ui/tools/requirements.txt | 2 - awx/network_ui/urls.py | 2 +- awx/network_ui/views.py | 67 ++++++++++- 14 files changed, 152 insertions(+), 209 deletions(-) rename awx/network_ui/{designs => docs}/README.md (100%) rename awx/network_ui/{designs => docs}/messages.yml (100%) rename awx/network_ui/{designs => docs}/models.png (100%) rename awx/network_ui/{designs => docs}/models.yml (100%) delete mode 100644 awx/network_ui/serializers.py delete mode 100644 awx/network_ui/static/network_ui/.gitignore delete mode 100644 awx/network_ui/static/network_ui/.jshintrc delete mode 100644 awx/network_ui/tools/Makefile delete mode 100644 awx/network_ui/tools/requirements.txt diff --git a/awx/network_ui/consumers.py b/awx/network_ui/consumers.py index 0cd2f82305..0d777def41 100644 --- a/awx/network_ui/consumers.py +++ b/awx/network_ui/consumers.py @@ -1,5 +1,4 @@ # Copyright (c) 2017 Red Hat, Inc -# In consumers.py from channels import Group, Channel from channels.sessions import channel_session from awx.network_ui.models import Topology, Device, Link, Client, Interface @@ -13,7 +12,6 @@ import logging from awx.network_ui.utils import transform_dict import json -# Connected to websocket.connect logger = logging.getLogger("awx.network_ui.consumers") @@ -31,7 +29,14 @@ def parse_inventory_id(data): class Persistence(object): + ''' + Provides database persistence for the topology canvas. + ''' + def parse_message_text(self, message_text, client_id): + ''' + See the Messages of CONTRIBUTING.md for the message format. + ''' data = json.loads(message_text) if len(data) == 2: message_type = data.pop(0) @@ -48,6 +53,10 @@ class Persistence(object): return None, None def handle(self, message): + ''' + Dispatches a message based on the message type to a handler function + of name onX where X is the message type. + ''' topology_id = message.get('topology') assert topology_id is not None, "No topology_id" client_id = message.get('client') @@ -73,83 +82,83 @@ class Persistence(object): y='y', name='name', type='device_type', - id='id', + id='cid', host_id='host_id'), device) logger.info("Device %s", device) - d, _ = Device.objects.get_or_create(topology_id=topology_id, id=device['id'], defaults=device) + d, _ = Device.objects.get_or_create(topology_id=topology_id, cid=device['cid'], defaults=device) d.x = device['x'] d.y = device['y'] d.device_type = device['device_type'] d.host_id = device['host_id'] d.save() (Topology.objects - .filter(topology_id=topology_id, device_id_seq__lt=device['id']) - .update(device_id_seq=device['id'])) + .filter(pk=topology_id, device_id_seq__lt=device['cid']) + .update(device_id_seq=device['cid'])) def onDeviceDestroy(self, device, topology_id, client_id): - Device.objects.filter(topology_id=topology_id, id=device['id']).delete() + Device.objects.filter(topology_id=topology_id, cid=device['id']).delete() def onDeviceMove(self, device, topology_id, client_id): - Device.objects.filter(topology_id=topology_id, id=device['id']).update(x=device['x'], y=device['y']) + Device.objects.filter(topology_id=topology_id, cid=device['id']).update(x=device['x'], y=device['y']) def onDeviceInventoryUpdate(self, device, topology_id, client_id): - Device.objects.filter(topology_id=topology_id, id=device['id']).update(host_id=device['host_id']) + Device.objects.filter(topology_id=topology_id, cid=device['id']).update(host_id=device['host_id']) def onDeviceLabelEdit(self, device, topology_id, client_id): - Device.objects.filter(topology_id=topology_id, id=device['id']).update(name=device['name']) + Device.objects.filter(topology_id=topology_id, cid=device['id']).update(name=device['name']) def onInterfaceLabelEdit(self, interface, topology_id, client_id): (Interface.objects .filter(device__topology_id=topology_id, - id=interface['id'], - device__id=interface['device_id']) + cid=interface['id'], + device__cid=interface['device_id']) .update(name=interface['name'])) def onLinkLabelEdit(self, link, topology_id, client_id): - Link.objects.filter(from_device__topology_id=topology_id, id=link['id']).update(name=link['name']) + Link.objects.filter(from_device__topology_id=topology_id, cid=link['id']).update(name=link['name']) def onInterfaceCreate(self, interface, topology_id, client_id): - Interface.objects.get_or_create(device_id=Device.objects.get(id=interface['device_id'], + Interface.objects.get_or_create(device_id=Device.objects.get(cid=interface['device_id'], topology_id=topology_id).pk, - id=interface['id'], + cid=interface['id'], defaults=dict(name=interface['name'])) (Device.objects - .filter(id=interface['device_id'], + .filter(cid=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 - .filter(topology_id=topology_id, id__in=[link['from_device_id'], link['to_device_id']]) - .values_list('id', 'pk')) - Link.objects.get_or_create(id=link['id'], + .filter(topology_id=topology_id, cid__in=[link['from_device_id'], link['to_device_id']]) + .values_list('cid', 'pk')) + Link.objects.get_or_create(cid=link['id'], name=link['name'], from_device_id=device_map[link['from_device_id']], to_device_id=device_map[link['to_device_id']], from_interface_id=Interface.objects.get(device_id=device_map[link['from_device_id']], - id=link['from_interface_id']).pk, + cid=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) + cid=link['to_interface_id']).pk) (Topology.objects - .filter(topology_id=topology_id, link_id_seq__lt=link['id']) + .filter(pk=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 - .filter(topology_id=topology_id, id__in=[link['from_device_id'], link['to_device_id']]) - .values_list('id', 'pk')) + .filter(topology_id=topology_id, cid__in=[link['from_device_id'], link['to_device_id']]) + .values_list('cid', 'pk')) if link['from_device_id'] not in device_map: return if link['to_device_id'] not in device_map: return - Link.objects.filter(id=link['id'], + Link.objects.filter(cid=link['id'], from_device_id=device_map[link['from_device_id']], to_device_id=device_map[link['to_device_id']], from_interface_id=Interface.objects.get(device_id=device_map[link['from_device_id']], - id=link['from_interface_id']).pk, + cid=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).delete() + cid=link['to_interface_id']).pk).delete() def onDeviceSelected(self, message_value, topology_id, client_id): 'Ignore DeviceSelected messages' @@ -178,20 +187,19 @@ class Persistence(object): @channel_session def ws_connect(message): - # Accept connection data = urlparse.parse_qs(message.content['query_string']) inventory_id = parse_inventory_id(data) - topology_ids = list(TopologyInventory.objects.filter(inventory_id=inventory_id).values_list('topology_id', flat=True)) + topology_ids = list(TopologyInventory.objects.filter(inventory_id=inventory_id).values_list('pk', flat=True)) topology_id = None if len(topology_ids) > 0: topology_id = topology_ids[0] if topology_id is not None: - topology = Topology.objects.get(topology_id=topology_id) + topology = Topology.objects.get(pk=topology_id) else: topology = Topology(name="topology", scale=1.0, panX=0, panY=0) topology.save() - TopologyInventory(inventory_id=inventory_id, topology_id=topology.topology_id).save() - topology_id = topology.topology_id + TopologyInventory(inventory_id=inventory_id, topology_id=topology.pk).save() + topology_id = topology.pk message.channel_session['topology_id'] = topology_id Group("topology-%s" % topology_id).add(message.reply_channel) client = Client() @@ -200,7 +208,7 @@ def ws_connect(message): Group("client-%s" % client.pk).add(message.reply_channel) message.reply_channel.send({"text": json.dumps(["id", client.pk])}) message.reply_channel.send({"text": json.dumps(["topology_id", topology_id])}) - topology_data = transform_dict(dict(topology_id='topology_id', + topology_data = transform_dict(dict(id='topology_id', name='name', panX='panX', panY='panY', @@ -218,26 +226,38 @@ def send_snapshot(channel, topology_id): for i in (Interface.objects .filter(device__topology_id=topology_id) .values()): + i = transform_dict(dict(cid='id', + device_id='device_id', + id='interface_id', + name='name'), i) interfaces[i['device_id']].append(i) devices = list(Device.objects.filter(topology_id=topology_id).values()) + devices = [transform_dict(dict(cid='id', + id='device_id', + device_type='device_type', + host_id='host_id', + name='name', + x='x', + y='y', + interface_id_seq='interface_id_seq'), x) for x in devices] for device in devices: device['interfaces'] = interfaces[device['device_id']] - links = [dict(id=x['id'], + links = [dict(id=x['cid'], name=x['name'], - from_device_id=x['from_device__id'], - to_device_id=x['to_device__id'], - from_interface_id=x['from_interface__id'], - to_interface_id=x['to_interface__id']) + from_device_id=x['from_device__cid'], + to_device_id=x['to_device__cid'], + from_interface_id=x['from_interface__cid'], + to_interface_id=x['to_interface__cid']) for x in list(Link.objects .filter(Q(from_device__topology_id=topology_id) | Q(to_device__topology_id=topology_id)) - .values('id', + .values('cid', 'name', - 'from_device__id', - 'to_device__id', - 'from_interface__id', - 'to_interface__id'))] + 'from_device__cid', + 'to_device__cid', + 'from_interface__cid', + 'to_interface__cid'))] snapshot = dict(sender=0, devices=devices, links=links) diff --git a/awx/network_ui/designs/README.md b/awx/network_ui/docs/README.md similarity index 100% rename from awx/network_ui/designs/README.md rename to awx/network_ui/docs/README.md diff --git a/awx/network_ui/designs/messages.yml b/awx/network_ui/docs/messages.yml similarity index 100% rename from awx/network_ui/designs/messages.yml rename to awx/network_ui/docs/messages.yml diff --git a/awx/network_ui/designs/models.png b/awx/network_ui/docs/models.png similarity index 100% rename from awx/network_ui/designs/models.png rename to awx/network_ui/docs/models.png diff --git a/awx/network_ui/designs/models.yml b/awx/network_ui/docs/models.yml similarity index 100% rename from awx/network_ui/designs/models.yml rename to awx/network_ui/docs/models.yml diff --git a/awx/network_ui/migrations/0001_initial.py b/awx/network_ui/migrations/0001_initial.py index f7c416e745..9b81e82455 100644 --- a/awx/network_ui/migrations/0001_initial.py +++ b/awx/network_ui/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.7 on 2018-03-12 18:34 +# Generated by Django 1.11.11 on 2018-03-23 20:43 from __future__ import unicode_literals from django.db import migrations, models @@ -11,42 +11,43 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('main', '0026_v330_emitted_events'), ] operations = [ migrations.CreateModel( name='Client', fields=[ - ('client_id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(primary_key=True, serialize=False)), ], ), migrations.CreateModel( name='Device', fields=[ - ('device_id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(primary_key=True, serialize=False)), ('name', models.CharField(blank=True, max_length=200)), ('x', models.IntegerField()), ('y', models.IntegerField()), - ('id', models.IntegerField()), + ('cid', models.IntegerField()), ('device_type', models.CharField(blank=True, max_length=200)), ('interface_id_seq', models.IntegerField(default=0)), - ('host_id', models.IntegerField(default=0)), + ('host', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='main.Host')), ], ), migrations.CreateModel( name='Interface', fields=[ - ('interface_id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(primary_key=True, serialize=False)), ('name', models.CharField(blank=True, max_length=200)), - ('id', models.IntegerField()), + ('cid', models.IntegerField()), ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Device')), ], ), migrations.CreateModel( name='Link', fields=[ - ('link_id', models.AutoField(primary_key=True, serialize=False)), - ('id', models.IntegerField()), + ('id', models.AutoField(primary_key=True, serialize=False)), + ('cid', models.IntegerField()), ('name', models.CharField(blank=True, max_length=200)), ('from_device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='from_link', to='network_ui.Device')), ('from_interface', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='from_link', to='network_ui.Interface')), @@ -57,7 +58,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Topology', fields=[ - ('topology_id', models.AutoField(primary_key=True, serialize=False)), + ('id', models.AutoField(primary_key=True, serialize=False)), ('name', models.CharField(blank=True, max_length=200)), ('scale', models.FloatField()), ('panX', models.FloatField()), @@ -69,8 +70,8 @@ class Migration(migrations.Migration): migrations.CreateModel( name='TopologyInventory', fields=[ - ('topology_inventory_id', models.AutoField(primary_key=True, serialize=False)), - ('inventory_id', models.IntegerField()), + ('id', models.AutoField(primary_key=True, serialize=False)), + ('inventory', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Inventory')), ('topology', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Topology')), ], ), diff --git a/awx/network_ui/models.py b/awx/network_ui/models.py index 46ba702dc5..07d87e26cc 100644 --- a/awx/network_ui/models.py +++ b/awx/network_ui/models.py @@ -3,15 +3,15 @@ from django.db import models class Device(models.Model): - device_id = models.AutoField(primary_key=True,) + id = models.AutoField(primary_key=True,) topology = models.ForeignKey('Topology',) name = models.CharField(max_length=200, blank=True) x = models.IntegerField() y = models.IntegerField() - id = models.IntegerField() + cid = models.IntegerField() device_type = models.CharField(max_length=200, blank=True) interface_id_seq = models.IntegerField(default=0,) - host_id = models.IntegerField(default=0,) + host = models.ForeignKey('main.Host', default=None, null=True, on_delete=models.SET_NULL) def __unicode__(self): return self.name @@ -19,18 +19,18 @@ class Device(models.Model): class Link(models.Model): - link_id = models.AutoField(primary_key=True,) + 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',) - id = models.IntegerField() + cid = models.IntegerField() name = models.CharField(max_length=200, blank=True) class Topology(models.Model): - topology_id = models.AutoField(primary_key=True,) + id = models.AutoField(primary_key=True,) name = models.CharField(max_length=200, blank=True) scale = models.FloatField() panX = models.FloatField() @@ -44,15 +44,15 @@ class Topology(models.Model): class Client(models.Model): - client_id = models.AutoField(primary_key=True,) + id = models.AutoField(primary_key=True,) class Interface(models.Model): - interface_id = models.AutoField(primary_key=True,) + id = models.AutoField(primary_key=True,) device = models.ForeignKey('Device',) name = models.CharField(max_length=200, blank=True) - id = models.IntegerField() + cid = models.IntegerField() def __unicode__(self): return self.name @@ -60,6 +60,6 @@ class Interface(models.Model): class TopologyInventory(models.Model): - topology_inventory_id = models.AutoField(primary_key=True,) + id = models.AutoField(primary_key=True,) topology = models.ForeignKey('Topology',) - inventory_id = models.IntegerField() + inventory = models.ForeignKey('main.Inventory') diff --git a/awx/network_ui/serializers.py b/awx/network_ui/serializers.py deleted file mode 100644 index 80884d2784..0000000000 --- a/awx/network_ui/serializers.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2017 Red Hat, Inc - -from awx.network_ui.models import Topology, Device, Link, Interface -from django.db.models import Q -import yaml -import json - -NetworkAnnotatedInterface = Interface.objects.values('name', - 'id', - 'from_link__pk', - 'to_link__pk', - 'from_link__to_device__name', - 'to_link__from_device__name', - 'from_link__to_interface__name', - 'to_link__from_interface__name') - - -def topology_data(topology_id): - - data = dict(devices=[], - links=[]) - - topology = Topology.objects.get(pk=topology_id) - - data['name'] = topology.name - data['topology_id'] = topology_id - - links = list(Link.objects - .filter(Q(from_device__topology_id=topology_id) | - Q(to_device__topology_id=topology_id))) - - interfaces = Interface.objects.filter(device__topology_id=topology_id) - - for device in Device.objects.filter(topology_id=topology_id).order_by('name'): - interfaces = list(NetworkAnnotatedInterface.filter(device_id=device.pk).order_by('name')) - interfaces = [dict(name=x['name'], - network=x['from_link__pk'] or x['to_link__pk'], - remote_device_name=x['from_link__to_device__name'] or x['to_link__from_device__name'], - remote_interface_name=x['from_link__to_interface__name'] or x['to_link__from_interface__name'], - id=x['id'], - ) for x in interfaces] - data['devices'].append(dict(name=device.name, - type=device.device_type, - x=device.x, - y=device.y, - id=device.id, - interfaces=interfaces)) - - for link in links: - data['links'].append(dict(from_device=link.from_device.name, - to_device=link.to_device.name, - from_interface=link.from_interface.name, - to_interface=link.to_interface.name, - from_device_id=link.from_device.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 - - -def yaml_serialize_topology(topology_id): - return yaml.safe_dump(topology_data(topology_id), default_flow_style=False) - - -def json_serialize_topology(topology_id): - return json.dumps(topology_data(topology_id)) diff --git a/awx/network_ui/static/network_ui/.gitignore b/awx/network_ui/static/network_ui/.gitignore deleted file mode 100644 index a93ebcc8ab..0000000000 --- a/awx/network_ui/static/network_ui/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/bundle.js -/node_modules -/style.css -/css -/js -/src-instrumented -/index-instrumented.html -/extracted diff --git a/awx/network_ui/static/network_ui/.jshintrc b/awx/network_ui/static/network_ui/.jshintrc deleted file mode 100644 index c8075a8ba8..0000000000 --- a/awx/network_ui/static/network_ui/.jshintrc +++ /dev/null @@ -1,47 +0,0 @@ -{ - "browser": true, - "node": true, - "jquery": true, - "esnext": true, - "globalstrict": true, - "curly": true, - "immed": true, - "latedef": "nofunc", - "noarg": true, - "nonew": true, - "maxerr": 10000, - "notypeof": true, - "globals": { - "$ENV": true, - "require": true, - "global": true, - "beforeEach": false, - "inject": false, - "module": false, - "angular":false, - "alert":false, - "$AnsibleConfig":true, - "$basePath":true, - "jsyaml":false, - "_":false, - "d3":false, - "Donut3D":false, - "nv":false, - "it": false, - "xit": false, - "expect": false, - "context": false, - "describe": false, - "moment": false, - "spyOn": false, - "jasmine": false - }, - "strict": false, - "quotmark": false, - "trailing": true, - "undef": true, - "unused": true, - "eqeqeq": true, - "indent": 4, - "newcap": false -} diff --git a/awx/network_ui/tools/Makefile b/awx/network_ui/tools/Makefile deleted file mode 100644 index c736a388b9..0000000000 --- a/awx/network_ui/tools/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -SERVER = "https://meganuke:8043" -PORT = "9000" - -.PHONY: clean coverage - -clean: - git clean -fdX . - git clean -fd . - -coverage: - ./coverage_report.py ${SERVER} - python -m SimpleHTTPServer ${PORT} diff --git a/awx/network_ui/tools/requirements.txt b/awx/network_ui/tools/requirements.txt deleted file mode 100644 index bfe4ae8612..0000000000 --- a/awx/network_ui/tools/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -docopt diff --git a/awx/network_ui/urls.py b/awx/network_ui/urls.py index a5f6ab9dbc..d08b0a448a 100644 --- a/awx/network_ui/urls.py +++ b/awx/network_ui/urls.py @@ -6,5 +6,5 @@ from awx.network_ui import views app_name = 'network_ui' urlpatterns = [ url(r'^topology.json$', views.json_topology_data, name='json_topology_data'), - url(r'^topology.yaml$', views.yaml_topology_data, name='json_topology_data'), + url(r'^topology.yaml$', views.yaml_topology_data, name='yaml_topology_data'), ] diff --git a/awx/network_ui/views.py b/awx/network_ui/views.py index 27ed47043b..e80f03c320 100644 --- a/awx/network_ui/views.py +++ b/awx/network_ui/views.py @@ -2,12 +2,73 @@ from django.shortcuts import render from django import forms from django.http import JsonResponse, HttpResponseBadRequest, HttpResponse -from awx.network_ui.models import Topology +from awx.network_ui.models import Topology, Device, Link, Interface +from django.db.models import Q import yaml +import json + +NetworkAnnotatedInterface = Interface.objects.values('name', + 'cid', + 'from_link__pk', + 'to_link__pk', + 'from_link__to_device__name', + 'to_link__from_device__name', + 'from_link__to_interface__name', + 'to_link__from_interface__name') -# Create your views here. -from .serializers import topology_data +def topology_data(topology_id): + + data = dict(devices=[], + links=[]) + + topology = Topology.objects.get(pk=topology_id) + + data['name'] = topology.name + data['topology_id'] = topology_id + + links = list(Link.objects + .filter(Q(from_device__topology_id=topology_id) | + Q(to_device__topology_id=topology_id))) + + interfaces = Interface.objects.filter(device__topology_id=topology_id) + + for device in Device.objects.filter(topology_id=topology_id).order_by('name'): + interfaces = list(NetworkAnnotatedInterface.filter(device_id=device.pk).order_by('name')) + interfaces = [dict(name=x['name'], + network=x['from_link__pk'] or x['to_link__pk'], + remote_device_name=x['from_link__to_device__name'] or x['to_link__from_device__name'], + remote_interface_name=x['from_link__to_interface__name'] or x['to_link__from_interface__name'], + id=x['cid'], + ) for x in interfaces] + data['devices'].append(dict(name=device.name, + type=device.device_type, + x=device.x, + y=device.y, + id=device.cid, + interfaces=interfaces)) + + for link in links: + data['links'].append(dict(from_device=link.from_device.name, + to_device=link.to_device.name, + from_interface=link.from_interface.name, + to_interface=link.to_interface.name, + from_device_id=link.from_device.cid, + to_device_id=link.to_device.cid, + from_interface_id=link.from_interface.cid, + to_interface_id=link.to_interface.cid, + name=link.name, + network=link.pk)) + + return data + + +def yaml_serialize_topology(topology_id): + return yaml.safe_dump(topology_data(topology_id), default_flow_style=False) + + +def json_serialize_topology(topology_id): + return json.dumps(topology_data(topology_id)) def index(request):