aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2019-03-14 15:51:19 -0400
committerParker Berberian <pberberian@iol.unh.edu>2019-04-15 11:04:18 -0400
commitf446907704b9cc0fee83cf4b4633cbfda03d2430 (patch)
treed18f606c447cd7eb7725521d336be2e6b184c795
parentd03ddca358c1f48389069e351be217fac514c69e (diff)
Fixing Network Models
Change-Id: Ia2cdf069e90c8091e8d984c368e47f375aed02ea Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
-rw-r--r--src/booking/quick_deployer.py33
-rw-r--r--src/resource_inventory/migrations/0009_auto_20190315_1757.py73
-rw-r--r--src/resource_inventory/models.py57
-rw-r--r--src/resource_inventory/resource_manager.py78
-rw-r--r--src/templates/resource/steps/pod_definition.html146
-rw-r--r--src/workflow/models.py94
-rw-r--r--src/workflow/resource_bundle_workflow.py214
7 files changed, 365 insertions, 330 deletions
diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py
index 8a81d18..f8dc9ff 100644
--- a/src/booking/quick_deployer.py
+++ b/src/booking/quick_deployer.py
@@ -22,7 +22,6 @@ from resource_inventory.models import (
Image,
GenericResourceBundle,
ConfigBundle,
- Vlan,
Host,
HostProfile,
HostConfiguration,
@@ -30,7 +29,10 @@ from resource_inventory.models import (
GenericHost,
GenericInterface,
OPNFVRole,
- OPNFVConfig
+ OPNFVConfig,
+ Network,
+ NetworkConnection,
+ NetworkRole
)
from resource_inventory.resource_manager import ResourceManager
from resource_inventory.pdf_templater import PDFTemplater
@@ -226,6 +228,20 @@ def check_invariants(request, **kwargs):
raise BookingLengthException("Booking must be between 1 and 21 days long")
+def configure_networking(grb, config):
+ # create network
+ net = Network.objects.create(name="public", bundle=grb, is_public=True)
+ # connect network to generic host
+ grb.getHosts()[0].generic_interfaces.first().connections.add(
+ NetworkConnection.objects.create(network=net, vlan_is_tagged=False)
+ )
+ # asign network role
+ role = NetworkRole.objects.create(name="public", network=net)
+ opnfv_config = config.opnfv_config.first()
+ if opnfv_config:
+ opnfv_config.networks.add(role)
+
+
def create_from_form(form, request):
quick_booking_id = str(uuid.uuid4())
@@ -267,18 +283,7 @@ def create_from_form(form, request):
generic_interface = GenericInterface.objects.create(profile=interface_profile, host=ghost)
generic_interface.save()
- # get vlan, assign to first interface
- publicnetwork = lab.vlan_manager.get_public_vlan()
- if not publicnetwork:
- raise NoRemainingPublicNetwork("No public networks were available for your pod")
- publicvlan = publicnetwork.vlan
- lab.vlan_manager.reserve_public_vlan(publicvlan)
-
- vlan = Vlan.objects.create(vlan_id=publicvlan, tagged=False, public=True)
- vlan.save()
-
- ghost.generic_interfaces.first().vlans.add(vlan)
- ghost.generic_interfaces.first().save()
+ configure_networking(grbundle, cbundle)
# generate resource bundle
resource_bundle = generate_resource_bundle(grbundle, cbundle)
diff --git a/src/resource_inventory/migrations/0009_auto_20190315_1757.py b/src/resource_inventory/migrations/0009_auto_20190315_1757.py
new file mode 100644
index 0000000..92ed0e9
--- /dev/null
+++ b/src/resource_inventory/migrations/0009_auto_20190315_1757.py
@@ -0,0 +1,73 @@
+# Generated by Django 2.1 on 2019-03-15 17:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resource_inventory', '0008_host_remote_management'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='NetworkConnection',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlan_is_tagged', models.BooleanField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='NetworkRole',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=100)),
+ ],
+ ),
+ migrations.RemoveField(
+ model_name='genericinterface',
+ name='vlans',
+ ),
+ migrations.RemoveField(
+ model_name='network',
+ name='vlan_id',
+ ),
+ migrations.AddField(
+ model_name='network',
+ name='bundle',
+ field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='networks', to='resource_inventory.GenericResourceBundle'),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='network',
+ name='is_public',
+ field=models.BooleanField(default=False),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='vlan',
+ name='network',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.Network'),
+ ),
+ migrations.AddField(
+ model_name='networkrole',
+ name='network',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network'),
+ ),
+ migrations.AddField(
+ model_name='networkconnection',
+ name='network',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network'),
+ ),
+ migrations.AddField(
+ model_name='genericinterface',
+ name='connections',
+ field=models.ManyToManyField(to='resource_inventory.NetworkConnection'),
+ ),
+ migrations.AddField(
+ model_name='opnfvconfig',
+ name='networks',
+ field=models.ManyToManyField(to='resource_inventory.NetworkRole'),
+ ),
+ ]
diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py
index 4e3974e..0b7b24c 100644
--- a/src/resource_inventory/models.py
+++ b/src/resource_inventory/models.py
@@ -105,26 +105,6 @@ class RamProfile(models.Model):
return str(self.amount) + "G for " + str(self.host)
-# Networking -- located here due to import order requirements
-class Network(models.Model):
- id = models.AutoField(primary_key=True)
- vlan_id = models.IntegerField()
- name = models.CharField(max_length=100)
-
- def __str__(self):
- return self.name
-
-
-class Vlan(models.Model):
- id = models.AutoField(primary_key=True)
- vlan_id = models.IntegerField()
- tagged = models.BooleanField()
- public = models.BooleanField(default=False)
-
- def __str__(self):
- return str(self.vlan_id) + ("_T" if self.tagged else "")
-
-
# Generic resource templates
class GenericResourceBundle(models.Model):
id = models.AutoField(primary_key=True)
@@ -145,6 +125,32 @@ class GenericResourceBundle(models.Model):
return self.name
+class Network(models.Model):
+ id = models.AutoField(primary_key=True)
+ name = models.CharField(max_length=100)
+ bundle = models.ForeignKey(GenericResourceBundle, on_delete=models.CASCADE, related_name="networks")
+ is_public = models.BooleanField()
+
+ def __str__(self):
+ return self.name
+
+
+class NetworkConnection(models.Model):
+ network = models.ForeignKey(Network, on_delete=models.CASCADE)
+ vlan_is_tagged = models.BooleanField()
+
+
+class Vlan(models.Model):
+ id = models.AutoField(primary_key=True)
+ vlan_id = models.IntegerField()
+ tagged = models.BooleanField()
+ public = models.BooleanField(default=False)
+ network = models.ForeignKey(Network, on_delete=models.DO_NOTHING, null=True)
+
+ def __str__(self):
+ return str(self.vlan_id) + ("_T" if self.tagged else "")
+
+
class GenericResource(models.Model):
bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.CASCADE)
hostname_validchars = RegexValidator(regex=r'(?=^.{1,253}$)(?=(^([A-Za-z0-9\-\_]{1,62}\.)*[A-Za-z0-9\-\_]{1,63}$))', message="Enter a valid hostname. Full domain name may be 1-253 characters, each hostname 1-63 characters (including suffixed dot), and valid characters for hostnames are A-Z, a-z, 0-9, hyphen (-), and underscore (_)")
@@ -185,14 +191,11 @@ class ResourceBundle(models.Model):
return "instance of " + str(self.template)
-# Networking
-
-
class GenericInterface(models.Model):
id = models.AutoField(primary_key=True)
- vlans = models.ManyToManyField(Vlan)
profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE)
host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces')
+ connections = models.ManyToManyField(NetworkConnection)
def __str__(self):
return "type " + str(self.profile) + " on host " + str(self.host)
@@ -224,6 +227,11 @@ class Opsys(models.Model):
return self.name
+class NetworkRole(models.Model):
+ name = models.CharField(max_length=100)
+ network = models.ForeignKey(Network, on_delete=models.CASCADE)
+
+
class ConfigBundle(models.Model):
id = models.AutoField(primary_key=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
@@ -240,6 +248,7 @@ class OPNFVConfig(models.Model):
installer = models.ForeignKey(Installer, on_delete=models.CASCADE)
scenario = models.ForeignKey(Scenario, on_delete=models.CASCADE)
bundle = models.ForeignKey(ConfigBundle, related_name="opnfv_config", on_delete=models.CASCADE)
+ networks = models.ManyToManyField(NetworkRole)
def __str__(self):
return "OPNFV job with " + str(self.installer) + " and " + str(self.scenario)
diff --git a/src/resource_inventory/resource_manager.py b/src/resource_inventory/resource_manager.py
index 52b0055..d3d3ed4 100644
--- a/src/resource_inventory/resource_manager.py
+++ b/src/resource_inventory/resource_manager.py
@@ -14,7 +14,14 @@ from dashboard.exceptions import (
ResourceProvisioningException,
ModelValidationException,
)
-from resource_inventory.models import Host, HostConfiguration, ResourceBundle, HostProfile
+from resource_inventory.models import (
+ Host,
+ HostConfiguration,
+ ResourceBundle,
+ HostProfile,
+ Network,
+ Vlan
+)
class ResourceManager:
@@ -66,39 +73,48 @@ class ResourceManager:
self.releaseHost(host)
resourceBundle.delete()
- def convertResourceBundle(self, genericResourceBundle, lab=None, config=None):
+ def get_vlans(self, genericResourceBundle):
+ networks = {}
+ vlan_manager = genericResourceBundle.lab.vlan_manager
+ for network in genericResourceBundle.networks.all():
+ if network.is_public:
+ public_net = vlan_manager.get_public_vlan()
+ vlan_manager.reserve_public_vlan(public_net.vlan)
+ networks[network.name] = public_net.vlan
+ else:
+ vlan = vlan_manager.get_vlan()
+ vlan_manager.reserve_vlans(vlan)
+ networks[network.name] = vlan
+ return networks
+
+ def convertResourceBundle(self, genericResourceBundle, config=None):
"""
Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
"""
- resource_bundle = ResourceBundle()
- resource_bundle.template = genericResourceBundle
- resource_bundle.save()
-
- hosts = genericResourceBundle.getHosts()
-
- # current supported case: user creating new booking
- # currently unsupported: editing existing booking
-
+ resource_bundle = ResourceBundle.objects.create(template=genericResourceBundle)
+ generic_hosts = genericResourceBundle.getHosts()
physical_hosts = []
- for host in hosts:
+ vlan_map = self.get_vlans(genericResourceBundle)
+
+ for generic_host in generic_hosts:
host_config = None
if config:
- host_config = HostConfiguration.objects.get(bundle=config, host=host)
+ host_config = HostConfiguration.objects.get(bundle=config, host=generic_host)
try:
- physical_host = self.acquireHost(host, genericResourceBundle.lab.name)
+ physical_host = self.acquireHost(generic_host, genericResourceBundle.lab.name)
except ResourceAvailabilityException:
- self.fail_acquire(physical_hosts)
+ self.fail_acquire(physical_hosts, vlan_map)
raise ResourceAvailabilityException("Could not provision hosts, not enough available")
try:
physical_host.bundle = resource_bundle
- physical_host.template = host
+ physical_host.template = generic_host
physical_host.config = host_config
physical_hosts.append(physical_host)
- self.configureNetworking(physical_host)
+ self.configureNetworking(physical_host, vlan_map)
except Exception:
- self.fail_acquire(physical_hosts)
+ self.fail_acquire(physical_hosts, vlan_map)
raise ResourceProvisioningException("Network configuration failed.")
try:
physical_host.save()
@@ -108,13 +124,20 @@ class ResourceManager:
return resource_bundle
- def configureNetworking(self, host):
+ def configureNetworking(self, host, vlan_map):
generic_interfaces = list(host.template.generic_interfaces.all())
for int_num, physical_interface in enumerate(host.interfaces.all()):
generic_interface = generic_interfaces[int_num]
physical_interface.config.clear()
- for vlan in generic_interface.vlans.all():
- physical_interface.config.add(vlan)
+ for connection in generic_interface.connections.all():
+ physical_interface.config.add(
+ Vlan.objects.create(
+ vlan_id=vlan_map[connection.network.name],
+ tagged=connection.vlan_is_tagged,
+ public=connection.network.is_public,
+ network=connection.network
+ )
+ )
# private interface
def acquireHost(self, genericHost, labName):
@@ -136,6 +159,17 @@ class ResourceManager:
host.booked = False
host.save()
- def fail_acquire(self, hosts):
+ def releaseNetworks(self, grb, vlan_manager, vlans):
+ for net_name, vlan_id in vlans.items():
+ net = Network.objects.get(name=net_name, bundle=grb)
+ if(net.is_public):
+ vlan_manager.release_public_vlan(vlan_id)
+ else:
+ vlan_manager.release_vlans(vlan_id)
+
+ def fail_acquire(self, hosts, vlans):
+ grb = hosts[0].template.resource.bundle
+ vlan_manager = hosts[0].lab.vlan_manager
+ self.releaseNetworks(grb, vlan_manager, vlans)
for host in hosts:
self.releaseHost(host)
diff --git a/src/templates/resource/steps/pod_definition.html b/src/templates/resource/steps/pod_definition.html
index 8599bb0..2cb6257 100644
--- a/src/templates/resource/steps/pod_definition.html
+++ b/src/templates/resource/steps/pod_definition.html
@@ -22,23 +22,9 @@ var netColors = ['red', 'blue', 'purple', 'green', 'orange', '#8CCDF5', '#1E9BAC
var hostCount = 0;
var lastHostBottom = 100;
var networks = new Set([]);
-var network_names = new Set([]);
var has_public_net = false;
-var vlans = {{vlans|default:'null'}};
-var vlan_string = "";
function main(graphContainer, overviewContainer, toolbarContainer) {
- if(vlans){
- for(var i=0; i<vlans.length-1; i++){
- vlan_string += vlans[i] + ", ";
- }
- if(vlans.length > 0){
- vlan_string += vlans[vlans.length-1];
- }
-
- var str = "Available vlans for your POD: " + vlan_string;
- document.getElementById("vlan_notice").innerHTML = str;
- }
//check if the browser is supported
if (!mxClient.isBrowserSupported()) {
mxUtils.error('Browser is not supported', 200, false);
@@ -55,14 +41,6 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
var model = graph.getModel();
editor.setGraphContainer(graphContainer);
- {% if debug %}
- editor.addAction('printXML', function(editor, cell) {
- mxLog.write(encodeGraph(graph));
- mxLog.show();
- });
- {% endif %}
-
-
doGlobalConfig(graph);
currentGraph = graph;
@@ -70,7 +48,6 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
restoreFromXml('{{xml|safe}}', editor);
{% elif hosts %}
{% for host in hosts %}
-
var host = {{host|safe}};
makeHost(host);
{% endfor %}
@@ -87,12 +64,15 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
addToolbarButton(editor, toolbarContainer, 'zoomOut', '', "/static/img/mxgraph/zoom_out.png", true);
{% if debug %}
+ editor.addAction('printXML', function(editor, cell) {
+ mxLog.write(encodeGraph(graph));
+ mxLog.show();
+ });
addToolbarButton(editor, toolbarContainer, 'printXML', '', '/static/img/mxgraph/fit_to_size.png', true);
{% endif %}
var outline = new mxOutline(graph, overviewContainer);
-
var checkAllowed = function(edge, terminal, source) {
//check if other terminal is null, and that they are different
otherTerminal = edge.getTerminal(!source);
@@ -145,14 +125,14 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
}
});
- createDeleteDialog = function(id)
- {
- var content = document.createElement('div');
- var innerHTML = "<button style='width: 46%;' onclick=deleteCell('" + id + "');>Remove</button>"
- innerHTML += "<button style='width: 46%;' onclick='currentWindow.destroy();'>Cancel</button>"
- content.innerHTML = innerHTML;
- showWindow(currentGraph, 'Do you want to delete this network?', content, 200, 62);
+ createDeleteDialog = function(id) {
+ var content = document.createElement('div');
+ var innerHTML = "<button style='width: 46%;' onclick=deleteCell('" + id + "');>Remove</button>"
+ innerHTML += "<button style='width: 46%;' onclick='currentWindow.destroy();'>Cancel</button>"
+ content.innerHTML = innerHTML;
+ showWindow(currentGraph, 'Do you want to delete this network?', content, 200, 62);
}
+
graph.dblClick = function(evt, cell) {
if( cell != null ){
@@ -167,8 +147,6 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
}
}
};
- graph.setCellsSelectable(false);
- graph.setCellsMovable(false);
updateHosts({{ removed_hosts|default:"[]"|safe }});
if(!has_public_net){
@@ -197,10 +175,8 @@ function restoreFromXml(xml, editor) {
var cell = root.getChildAt(i);
if(cell.getId().indexOf("network") > -1) {
var info = JSON.parse(cell.getValue());
- var vlan_id = info['vlan_id'];
- networks.add(vlan_id);
var name = info['name'];
- network_names.add(name);
+ networks.add(name);
var styles = cell.getStyle().split(";");
var color = null;
for(var j=0; j< styles.length; j++){
@@ -211,11 +187,10 @@ function restoreFromXml(xml, editor) {
}
}
if(info.public){
- vlan_id = "";
has_public_net = true;
}
netCount++;
- makeSidebarNetwork(name, vlan_id, color, cell.getId());
+ makeSidebarNetwork(name, color, cell.getId());
}
}
}
@@ -228,50 +203,27 @@ function deleteCell(cellId) {
}
currentGraph.removeCells([cell]);
currentWindow.destroy();
-
}
function newNetworkWindow() {
var innerHtml = 'Name: <input type="text" name="net_name" id="net_name_input" style="margin:5px;"><br>';
- innerHtml += 'Vlan: <input type="number" step="1" name="vlan_id" id="vlan_id_input" style="margin:5px;"><br>';
innerHtml += '<button style="width: 46%;" onclick="parseNetworkWindow()">Okay</button>';
innerHtml += '<button style="width: 46%;" onclick="currentWindow.destroy();">Cancel</button><br>';
- innerHtml += '<div id="current_window_vlans"/>';
innerHtml += '<div id="current_window_errors"/>';
var content = document.createElement("div");
content.innerHTML = innerHtml;
showWindow(currentGraph, "Network Creation", content, 300, 300);
-
- if(vlans){
- vlan_notice = document.getElementById("current_window_vlans");
- vlan_notice.appendChild(document.createTextNode("Available Vlans: " + vlan_string));
- }
}
function parseNetworkWindow() {
var net_name = document.getElementById("net_name_input").value
- var vlan_id = document.getElementById("vlan_id_input").value
var error_div = document.getElementById("current_window_errors");
- var vlan_valid = Number.isInteger(Number(vlan_id)) && (vlan_id < 4095) && (vlan_id > 1)
- if(vlans){
- vlan_valid = vlan_valid & vlans.indexOf(Number(vlan_id)) >= 0;
- }
- if( !vlan_valid)
- {
- error_div.innerHTML = "Please only enter an integer in the valid range (default 1-4095) for the VLAN ID";
- return;
- }
- if( networks.has(vlan_id))
- {
- error_div.innerHTML = "All VLAN IDs must be unique";
- return;
- }
- if( network_names.has(net_name) ){
+ if( networks.has(net_name) ){
error_div.innerHTML = "All network names must be unique";
return;
}
- addNetwork(net_name, vlan_id);
+ addNetwork(net_name);
currentWindow.destroy();
}
@@ -312,6 +264,12 @@ function encodeGraph(graph) {
function doGlobalConfig(graph) {
//general graph stuff
graph.setMultigraph(false);
+ graph.setCellsSelectable(false);
+ graph.setCellsMovable(false);
+
+ //testing
+ graph.vertexLabelIsMovable = true;
+
//edge behavior
graph.setConnectable(true);
@@ -332,6 +290,9 @@ function doGlobalConfig(graph) {
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
+ hostStyle = graph.getStylesheet().getDefaultVertexStyle();
+ hostStyle[mxConstants.STYLE_ROUNDED] = 1;
+
// TODO: Proper override
graph.convertValueToString = function(cell) {
try{
@@ -395,29 +356,21 @@ function othersUntagged(edgeID) {
var end1 = edge.getTerminal(true);
var end2 = edge.getTerminal(false);
- if( end1.getParent().getId().split('_')[0] == 'host' )
- {
+ if( end1.getParent().getId().split('_')[0] == 'host' ){
var netint = end1;
- }
- else
- {
+ } else {
var netint = end2;
}
var edges = netint.edges;
- for( var i=0; i < edges.length; i++ )
- {
- if( edges[i].getValue() )
- {
+ for( var i=0; i < edges.length; i++ ) {
+ if( edges[i].getValue() ) {
var tagged = JSON.parse(edges[i].getValue()).tagged;
- }
- else
- {
+ } else {
var tagged = true;
}
- if( !tagged )
- {
+ if( !tagged ) {
return true;
}
}
@@ -454,12 +407,11 @@ function parseVlanWindow(edgeID) {
break;
}
}
- //edge.setValue(cellValue);
currentGraph.refresh(edge);
closeWindow();
}
-function makeMxNetwork(vlan_id, net_name) {
+function makeMxNetwork(net_name, public = false) {
model = currentGraph.getModel();
width = 10;
height = 1700;
@@ -472,9 +424,8 @@ function makeMxNetwork(vlan_id, net_name) {
//alert(color);
}
var net_val = Object();
- net_val['vlan_id'] = vlan_id;
net_val['name'] = net_name;
- net_val['public'] = vlan_id < 0;
+ net_val['public'] = public;
net = currentGraph.insertVertex(
currentGraph.getDefaultParent(),
'network_' + netCount,
@@ -505,23 +456,23 @@ function makeMxNetwork(vlan_id, net_name) {
retVal['color'] = color;
retVal['element_id'] = "network_" + netCount;
+ networks.add(net_name);
+
netCount++;
return retVal;
}
function addPublicNetwork() {
- var net = makeMxNetwork(-1, "public");
- network_names.add("public");
- makeSidebarNetwork("public", "", net['color'], net['element_id']);
+ var net = makeMxNetwork("public", true);
+ makeSidebarNetwork("public", net['color'], net['element_id']);
+ has_public_net = true;
}
-function addNetwork(net_name, vlan_id) {
- var ret = makeMxNetwork(vlan_id, net_name);
+function addNetwork(net_name) {
+ var ret = makeMxNetwork(net_name);
var color = ret['color'];
var net_id = ret['element_id'];
- networks.add(vlan_id);
- network_names.add(net_name);
- makeSidebarNetwork(net_name, vlan_id, color, net_id);
+ makeSidebarNetwork(net_name, color, net_id);
}
function updateHosts(removed) {
@@ -535,8 +486,7 @@ function updateHosts(removed) {
var hosts = currentGraph.getChildVertices(currentGraph.getDefaultParent());
var topdist = 100;
- for(var i=0; i<hosts.length; i++)
- {
+ for(var i=0; i<hosts.length; i++) {
var host = hosts[i];
if(!host.id.startsWith("host_"))
{
@@ -549,7 +499,7 @@ function updateHosts(removed) {
}
}
-function makeSidebarNetwork(net_name, vlan_id, color, net_id){
+function makeSidebarNetwork(net_name, color, net_id){
var newNet = document.createElement("li");
var colorBlob = document.createElement("div");
colorBlob.className = "colorblob";
@@ -565,9 +515,6 @@ function makeSidebarNetwork(net_name, vlan_id, color, net_id){
createDeleteDialog(net_id);
}, false);
var text = net_name;
- if(vlan_id){
- text += " : " + vlan_id;
- }
var newNetValue = document.createTextNode(text);
textContainer.appendChild(newNetValue);
colorBlob.style['background'] = color;
@@ -585,7 +532,7 @@ function makeHost(hostInfo) {
interfaces = hostInfo['interfaces'];
graph = currentGraph;
width = 100;
- height = (25 * interfaces.length) + 10;
+ height = (25 * interfaces.length) + 25;
xoff = 75;
yoff = lastHostBottom + 50;
lastHostBottom = yoff + height;
@@ -600,6 +547,7 @@ function makeHost(hostInfo) {
'editable=0',
false
);
+ host.getGeometry().offset = new mxPoint(-50,0);
host.setConnectable(false);
hostCount++;
@@ -609,13 +557,16 @@ function makeHost(hostInfo) {
null,
JSON.stringify(interfaces[i]),
90,
- (i * 25) + 5,
+ (i * 25) + 12,
20,
20,
'fillColor=blue;editable=0',
false
);
+ port.getGeometry().offset = new mxPoint(-4*interfaces[i].name.length -2,0);
+ currentGraph.refresh(port);
}
+ currentGraph.refresh(host);
}
function submitForm() {
@@ -709,7 +660,6 @@ function submitForm() {
</div>
<ul id="network_list">
</ul>
- <p id="vlan_notice"></p>
<button type="button" style="display: none" onclick="submitForm();">Submit</button>
</div>
<form id="xml_form" method="post" action="/wf/workflow/">
diff --git a/src/workflow/models.py b/src/workflow/models.py
index cdfddef..3784fe1 100644
--- a/src/workflow/models.py
+++ b/src/workflow/models.py
@@ -203,27 +203,6 @@ class Confirmation_Step(WorkflowStep):
title = "Confirm Changes"
description = "Does this all look right?"
- def get_vlan_warning(self):
- grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
- if not grb:
- return 0
- if self.repo.BOOKING_MODELS not in self.repo.el:
- return 0
- vlan_manager = grb.lab.vlan_manager
- if vlan_manager is None:
- return 0
- hosts = grb.getHosts()
- for host in hosts:
- for interface in host.generic_interfaces.all():
- for vlan in interface.vlans.all():
- if vlan.public:
- if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
- return 1
- else:
- if not vlan_manager.is_available(vlan.vlan_id):
- return 1 # There is a problem with these vlans
- return 0
-
def get_context(self):
context = super(Confirmation_Step, self).get_context()
context['form'] = ConfirmationForm()
@@ -231,7 +210,6 @@ class Confirmation_Step(WorkflowStep):
self.repo_get(self.repo.CONFIRMATION),
default_flow_style=False
).strip()
- context['vlan_warning'] = self.get_vlan_warning()
return context
@@ -262,33 +240,8 @@ class Confirmation_Step(WorkflowStep):
pass
else:
- if "vlan_input" in request.POST:
- if request.POST.get("vlan_input") == "True":
- self.translate_vlans()
- return self.render(request)
pass
- def translate_vlans(self):
- grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
- if not grb:
- return 0
- vlan_manager = grb.lab.vlan_manager
- if vlan_manager is None:
- return 0
- hosts = grb.getHosts()
- for host in hosts:
- for interface in host.generic_interfaces.all():
- for vlan in interface.vlans.all():
- if not vlan.public:
- if not vlan_manager.is_available(vlan.vlan_id):
- vlan.vlan_id = vlan_manager.get_vlan()
- vlan.save()
- else:
- if not vlan_manager.public_vlan_is_available(vlan.vlan_id):
- pub_vlan = vlan_manager.get_public_vlan()
- vlan.vlan_id = pub_vlan.vlan
- vlan.save()
-
class Workflow():
@@ -453,6 +406,11 @@ class Repository():
except Exception as e:
return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
+ if 'networks' in models:
+ for net in models['networks'].values():
+ net.bundle = bundle
+ net.save()
+
if 'interfaces' in models:
for interface_set in models['interfaces'].values():
for interface in interface_set:
@@ -464,20 +422,21 @@ class Repository():
else:
return "GRB, no interface set provided. CODE:0x001a"
- if 'vlans' in models:
- for resource_name, mapping in models['vlans'].items():
- for profile_name, vlan_set in mapping.items():
+ if 'connections' in models:
+ for resource_name, mapping in models['connections'].items():
+ for profile_name, connection_set in mapping.items():
interface = GenericInterface.objects.get(
profile__name=profile_name,
host__resource__name=resource_name,
host__resource__bundle=models['bundle']
)
- for vlan in vlan_set:
+ for connection in connection_set:
try:
- vlan.save()
- interface.vlans.add(vlan)
+ connection.network = connection.network
+ connection.save()
+ interface.connections.add(connection)
except Exception as e:
- return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017"
+ return "GRB, saving vlan " + str(connection) + " failed. Exception: " + str(e) + ". CODE:0x0017"
else:
return "GRB, no vlan set provided. CODE:0x0018"
@@ -534,9 +493,6 @@ class Repository():
else:
return "BOOK, no selected resource. CODE:0x000e"
- if not self.reserve_vlans(selected_grb):
- return "BOOK, vlans not available"
-
if 'booking' in models:
booking = models['booking']
else:
@@ -593,30 +549,6 @@ class Repository():
except Exception as e:
return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016"
- def reserve_vlans(self, grb):
- """
- True is success
- """
- vlans = []
- public_vlan = None
- vlan_manager = grb.lab.vlan_manager
- if vlan_manager is None:
- return True
- for host in grb.getHosts():
- for interface in host.generic_interfaces.all():
- for vlan in interface.vlans.all():
- if vlan.public:
- public_vlan = vlan
- else:
- vlans.append(vlan.vlan_id)
-
- try:
- vlan_manager.reserve_vlans(vlans)
- vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
- return True
- except Exception:
- return False
-
def __init__(self):
self.el = {}
self.el[self.CONFIRMATION] = {}
diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py
index 4858ebe..536187f 100644
--- a/src/workflow/resource_bundle_workflow.py
+++ b/src/workflow/resource_bundle_workflow.py
@@ -10,6 +10,7 @@
from django.shortcuts import render
from django.forms import formset_factory
+from django.conf import settings
import json
import re
@@ -25,11 +26,12 @@ from workflow.forms import (
)
from resource_inventory.models import (
GenericResourceBundle,
- Vlan,
GenericInterface,
GenericHost,
GenericResource,
- HostProfile
+ HostProfile,
+ Network,
+ NetworkConnection
)
from dashboard.exceptions import (
InvalidVlanConfigurationException,
@@ -185,6 +187,7 @@ class Define_Nets(WorkflowStep):
hostlist = self.repo_get(self.repo.GRB_LAST_HOSTLIST, None)
added_list = []
added_dict = {}
+ context['debug'] = settings.DEBUG
context['added_hosts'] = []
if hostlist is not None:
new_hostlist = []
@@ -239,15 +242,15 @@ class Define_Nets(WorkflowStep):
self.metastep.set_valid("Networks applied successfully")
except ResourceAvailabilityException:
self.metastep.set_invalid("Public network not availble")
- except Exception:
- self.metastep.set_invalid("An error occurred when applying networks")
+ except Exception as e:
+ self.metastep.set_invalid("An error occurred when applying networks: " + str(e))
return self.render(request)
def updateModels(self, xmlData):
models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- models["vlans"] = {}
- given_hosts, interfaces = self.parseXml(xmlData)
- vlan_manager = models['bundle'].lab.vlan_manager
+ models["connections"] = {}
+ models['networks'] = {}
+ given_hosts, interfaces, networks = self.parseXml(xmlData)
existing_host_list = models.get("hosts", [])
existing_hosts = {} # maps id to host
for host in existing_host_list:
@@ -255,104 +258,133 @@ class Define_Nets(WorkflowStep):
bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER)))
+ for net_id, net in networks.items():
+ network = Network()
+ network.name = net['name']
+ network.bundle = bundle
+ network.is_public = net['public']
+ models['networks'][net_id] = network
+
for hostid, given_host in given_hosts.items():
existing_host = existing_hosts[hostid[5:]]
for ifaceId in given_host['interfaces']:
iface = interfaces[ifaceId]
- if existing_host.resource.name not in models['vlans']:
- models['vlans'][existing_host.resource.name] = {}
- models['vlans'][existing_host.resource.name][iface['profile_name']] = []
- for network in iface['networks']:
- vlan_id = network['network']['vlan']
- is_public = network['network']['public']
- if is_public:
- public_net = vlan_manager.get_public_vlan()
- if public_net is None:
- raise ResourceAvailabilityException("No public networks available")
- vlan_id = vlan_manager.get_public_vlan().vlan
- vlan = Vlan(vlan_id=vlan_id, tagged=network['tagged'], public=is_public)
- models['vlans'][existing_host.resource.name][iface['profile_name']].append(vlan)
+ if existing_host.resource.name not in models['connections']:
+ models['connections'][existing_host.resource.name] = {}
+ models['connections'][existing_host.resource.name][iface['profile_name']] = []
+ for connection in iface['connections']:
+ network_id = connection['network']
+ net = models['networks'][network_id]
+ connection = NetworkConnection(vlan_is_tagged=connection['tagged'], network=net)
+ models['connections'][existing_host.resource.name][iface['profile_name']].append(connection)
bundle.xml = xmlData
self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
- # serialize and deserialize xml from mxGraph
- def parseXml(self, xmlString):
- parent_nets = {} # map network ports to networks
- networks = {} # maps net id to network object
- hosts = {} # cotains id -> hosts, each containing interfaces, referencing networks
- interfaces = {} # maps id -> interface
+ def decomposeXml(self, xmlString):
+ """
+ This function takes in an xml doc from our front end
+ and returns dictionaries that map cellIds to the xml
+ nodes themselves. There is no unpacking of the
+ xml objects, just grouping and organizing
+ """
+
+ connections = {}
+ networks = {}
+ hosts = {}
+ interfaces = {}
+ network_ports = {}
+
xmlDom = minidom.parseString(xmlString)
root = xmlDom.documentElement.firstChild
- netids = {}
- untagged_ints = {}
for cell in root.childNodes:
cellId = cell.getAttribute('id')
+ group = cellId.split("_")[0]
+ parentGroup = cell.getAttribute("parent").split("_")[0]
+ # place cell into correct group
if cell.getAttribute("edge"):
- # cell is a network connection
- escaped_json_str = cell.getAttribute("value")
- json_str = escaped_json_str.replace('&quot;', '"')
- attributes = json.loads(json_str)
- tagged = attributes['tagged']
- interface = None
- network = None
- src = cell.getAttribute("source")
- tgt = cell.getAttribute("target")
- if src in parent_nets:
- # src is a network port
- network = networks[parent_nets[src]]
- if tgt in untagged_ints and not tagged:
- raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
- interface = interfaces[tgt]
- untagged_ints[tgt] = True
- else:
- network = networks[parent_nets[tgt]]
- if src in untagged_ints and not tagged:
- raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
- interface = interfaces[src]
- untagged_ints[src] = True
- interface['networks'].append({"network": network, "tagged": tagged})
-
- elif "network" in cellId: # cell is a network
- escaped_json_str = cell.getAttribute("value")
- json_str = escaped_json_str.replace('&quot;', '"')
- net_info = json.loads(json_str)
- nid = net_info['vlan_id']
- public = net_info['public']
- try:
- int_netid = int(nid)
- assert public or int_netid > 1, "Net id is 1 or lower"
- assert int_netid < 4095, "Net id is 4095 or greater"
- except Exception:
- raise InvalidVlanConfigurationException("VLAN ID is not an integer more than 1 and less than 4095")
- if nid in netids:
- raise NetworkExistsException("Non unique network id found")
- else:
- pass
- network = {"name": net_info['name'], "vlan": net_info['vlan_id'], "public": public}
- netids[net_info['vlan_id']] = True
- networks[cellId] = network
-
- elif "host" in cellId: # cell is a host/machine
- # TODO gather host info
- cell_json_str = cell.getAttribute("value")
- cell_json = json.loads(cell_json_str)
- host = {"interfaces": [], "name": cellId, "profile_name": cell_json['name']}
- hosts[cellId] = host
-
- elif cell.hasAttribute("parent"):
- parentId = cell.getAttribute('parent')
- if "network" in parentId:
- parent_nets[cellId] = parentId
- elif "host" in parentId:
- # TODO gather iface info
- cell_json_str = cell.getAttribute("value")
- cell_json = json.loads(cell_json_str)
- iface = {"name": cellId, "networks": [], "profile_name": cell_json['name']}
- hosts[parentId]['interfaces'].append(cellId)
- interfaces[cellId] = iface
- return hosts, interfaces
+ connections[cellId] = cell
+
+ elif "network" in group:
+ networks[cellId] = cell
+
+ elif "host" in group:
+ hosts[cellId] = cell
+
+ elif "host" in parentGroup:
+ interfaces[cellId] = cell
+
+ # make network ports also map to thier network
+ elif "network" in parentGroup:
+ network_ports[cellId] = cell.getAttribute("parent") # maps port ID to net ID
+
+ return connections, networks, hosts, interfaces, network_ports
+
+ # serialize and deserialize xml from mxGraph
+ def parseXml(self, xmlString):
+ networks = {} # maps net name to network object
+ hosts = {} # cotains id -> hosts, each containing interfaces, referencing networks
+ interfaces = {} # maps id -> interface
+ untagged_ifaces = set() # used to check vlan config
+ network_names = set() # used to check network names
+ xml_connections, xml_nets, xml_hosts, xml_ifaces, xml_ports = self.decomposeXml(xmlString)
+
+ # parse Hosts
+ for cellId, cell in xml_hosts.items():
+ cell_json_str = cell.getAttribute("value")
+ cell_json = json.loads(cell_json_str)
+ host = {"interfaces": [], "name": cellId, "profile_name": cell_json['name']}
+ hosts[cellId] = host
+
+ # parse networks
+ for cellId, cell in xml_nets.items():
+ escaped_json_str = cell.getAttribute("value")
+ json_str = escaped_json_str.replace('&quot;', '"')
+ net_info = json.loads(json_str)
+ net_name = net_info['name']
+ public = net_info['public']
+ if net_name in network_names:
+ raise NetworkExistsException("Non unique network name found")
+ network = {"name": net_name, "public": public, "id": cellId}
+ networks[cellId] = network
+ network_names.add(net_name)
+
+ # parse interfaces
+ for cellId, cell in xml_ifaces.items():
+ parentId = cell.getAttribute('parent')
+ cell_json_str = cell.getAttribute("value")
+ cell_json = json.loads(cell_json_str)
+ iface = {"name": cellId, "connections": [], "profile_name": cell_json['name']}
+ hosts[parentId]['interfaces'].append(cellId)
+ interfaces[cellId] = iface
+
+ # parse connections
+ for cellId, cell in xml_connections.items():
+ escaped_json_str = cell.getAttribute("value")
+ json_str = escaped_json_str.replace('&quot;', '"')
+ attributes = json.loads(json_str)
+ tagged = attributes['tagged']
+ interface = None
+ network = None
+ src = cell.getAttribute("source")
+ tgt = cell.getAttribute("target")
+ if src in interfaces:
+ interface = interfaces[src]
+ network = networks[xml_ports[tgt]]
+ else:
+ interface = interfaces[tgt]
+ network = networks[xml_ports[src]]
+
+ if not tagged:
+ if interface['name'] in untagged_ifaces:
+ raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
+ untagged_ifaces.add(interface['name'])
+
+ # add connection to interface
+ interface['connections'].append({"tagged": tagged, "network": network['id']})
+
+ return hosts, interfaces, networks
class Resource_Meta_Info(WorkflowStep):