Reordere service graph and added tenant names
Change-Id: I5f8ac36f5b14e5e9d767202e0829d9f5c5458c38
diff --git a/views/ngXosViews/serviceGrid/src/css/dev.css b/views/ngXosViews/serviceGrid/src/css/dev.css
index b833a9f..e69de29 100644
--- a/views/ngXosViews/serviceGrid/src/css/dev.css
+++ b/views/ngXosViews/serviceGrid/src/css/dev.css
@@ -1,5 +0,0 @@
-#xosServiceGrid{
- position: absolute;
- top: 100px;
- left: 200px;
-}
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/css/main.css b/views/ngXosViews/serviceGrid/src/css/main.css
index c36e56d..c95ce7d 100644
--- a/views/ngXosViews/serviceGrid/src/css/main.css
+++ b/views/ngXosViews/serviceGrid/src/css/main.css
@@ -32,18 +32,20 @@
visibility: hidden;
transform: translate3d(100%, 0, 0); } }
-#xosServiceGrid service-graph {
- display: block;
- width: 100%;
- height: 600px; }
-
-#xosServiceGrid .node {
- stroke: #337ab7;
- fill: white; }
-
-#xosServiceGrid .node.xos {
- fill: #d9534f; }
-
-#xosServiceGrid .link {
- stroke: black;
- stroke-width: 2px; }
+#xosServiceGrid {
+ width: 100%; }
+ #xosServiceGrid service-graph {
+ display: block;
+ width: 100%;
+ height: 600px; }
+ #xosServiceGrid .node {
+ stroke: #337ab7;
+ fill: white; }
+ #xosServiceGrid .node.xos {
+ fill: #d9534f; }
+ #xosServiceGrid .link {
+ stroke: #286090;
+ stroke-width: 2px; }
+ #xosServiceGrid .link-arrow {
+ stroke: #286090;
+ fill: #286090; }
diff --git a/views/ngXosViews/serviceGrid/src/js/service-graph.js b/views/ngXosViews/serviceGrid/src/js/service-graph.js
index 904466c..649be6d 100644
--- a/views/ngXosViews/serviceGrid/src/js/service-graph.js
+++ b/views/ngXosViews/serviceGrid/src/js/service-graph.js
@@ -71,50 +71,100 @@
let svg;
let el = $element[0];
- let node;
- let link;
+ let node, nodes;
+ let link, links;
const _this = this;
this.panelConfig = {
position: 'right'
};
+ // find position for link labels
+ const xpos = (source, target) => {
+ if (target.x > source.x) {
+ return (source.x + (target.x - source.x)/2); }
+ else {
+ return (target.x + (source.x - target.x)/2); }
+ }
+
+ const ypos = (source, target) => {
+ if (target.y > source.y) {
+ return Math.round(source.y + (target.y - source.y)/2);
+ }
+ else {
+ return Math.round(target.y + (source.y - target.y)/2); }
+ }
+
// animate node and links in the correct place
- const tick = (e) => {
+ const tick = () => {
node
- .attr('cx', d => d.x)
- .attr('cy', d => d.y)
- .attr({
- transform: d => `translate(${d.x}, ${d.y})`
- });
+ .attr('cx', d => d.x)
+ .attr('cy', d => d.y)
+ .attr({
+ transform: d => `translate(${d.x}, ${d.y})`
+ });
- link
- .attr('x1', d => d.source.x)
- .attr('y1', d => d.source.y)
- .attr('x2', d => getTargetNodeCircumferencePoint(d)[0])
- .attr('y2', d => getTargetNodeCircumferencePoint(d)[1]);
+ svg.selectAll('.link')
+ .attr('x1', d => d.source.x)
+ .attr('y1', d => d.source.y)
+ .attr('x2', d => getTargetNodeCircumferencePoint(d)[0])
+ .attr('y2', d => getTargetNodeCircumferencePoint(d)[1]);
+
+ svg.selectAll('.link-text')
+ .attr('x', d => xpos(d.source, {x: getTargetNodeCircumferencePoint(d)[0], y: getTargetNodeCircumferencePoint(d)[1]}))
+ .attr('y', d => ypos(d.source, {x: getTargetNodeCircumferencePoint(d)[0], y: getTargetNodeCircumferencePoint(d)[1]}))
+ .attr('transform', d => {
+ let x = xpos(d.source, {x: getTargetNodeCircumferencePoint(d)[0], y: getTargetNodeCircumferencePoint(d)[1]});
+ let y = ypos(d.source, {x: getTargetNodeCircumferencePoint(d)[0], y: getTargetNodeCircumferencePoint(d)[1]});
+ return `rotate(-30, ${x}, ${y})`;
+ });
+ };
+
+ var chainCount = 1;
+ var spareCount = 1;
+
+ const getNodePosition = (n, graph, chainElements) => {
+ let node = graph.node(n);
+ const step = el.clientWidth / (chainElements + 1);
+
+ if(graph.nodeEdges(n).length > 0){
+ let pos = {
+ y: el.clientHeight / 4,
+ x: step * chainCount,
+ fixed: true
+ };
+ angular.extend(node, pos);
+ chainCount = chainCount + 1;
+ }
+ else {
+ let pos = {
+ y: (el.clientHeight / 2) + (el.clientHeight / 4),
+ x: (step + step / 2) * spareCount,
+ fixed: true
+ };
+ angular.extend(node, pos);
+ spareCount = spareCount + 1;
+ }
+ return node;
};
Graph.getGraph().$promise
.then((graph) => {
// build links
- let links = graph.edges().map(e => {
+ links = graph.edges().map(e => {
return {
source: graph.node(e.v),
- target: graph.node(e.w)
+ target: graph.node(e.w),
+ tenant: graph.edge(e)
}
});
- let nodes = graph.nodes().map(n => graph.node(n));
+
+ // check how many nodes are connected
+ // let longerGraph = graphlib.alg.components(graph).filter(g => g.length > 1);
+ const longerGraph = graphlib.alg.components(graph).reduce(function (a, b) { return a.length > b.length ? a : b; }).length;
- //add xos as a node
- nodes.push({
- name: 'XOS',
- type: 'xos',
- x: el.clientWidth / 2,
- y: el.clientHeight / 2,
- fixed: true
- });
+ nodes = graph.nodes().reverse().map(n => getNodePosition(n, graph, longerGraph));
handleSvg(el);
defineArrows();
@@ -129,11 +179,23 @@
.on('tick', tick)
.start();
- link = svg.selectAll('.link')
- .data(links).enter().insert('line')
+ link = svg.selectAll('.link-container')
+ .data(links).enter().insert('g')
+ .attr('class', 'link-container');
+
+ link.insert('line')
.attr('class', 'link')
.attr('marker-end', 'url(#arrow)');
+ var linkText = svg.selectAll('.link-container')
+ .data(force.links())
+ .insert('text')
+ .attr({
+ class: 'link-text',
+ 'text-anchor': 'start'
+ })
+ .text(d => `${d.tenant.humanReadableName}`)
+
node = svg.selectAll('.node')
.data(nodes)
.enter().append('g')
@@ -211,6 +273,7 @@
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
+ .attr('class', 'link-arrow')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5');
};
diff --git a/views/ngXosViews/serviceGrid/src/sass/main.scss b/views/ngXosViews/serviceGrid/src/sass/main.scss
index e892746..e52991a 100644
--- a/views/ngXosViews/serviceGrid/src/sass/main.scss
+++ b/views/ngXosViews/serviceGrid/src/sass/main.scss
@@ -2,6 +2,7 @@
@import './side-panel.scss';
#xosServiceGrid {
+ width: 100%;
service-graph {
display: block;
width: 100%;
@@ -18,7 +19,12 @@
}
.link {
- stroke: black;
+ stroke: darken($brand-primary, 10);
stroke-width: 2px;
}
+
+ .link-arrow {
+ stroke: darken($brand-primary, 10);
+ fill: darken($brand-primary, 10);
+ }
}
\ No newline at end of file
diff --git a/xos/core/xoslib/static/css/xosServiceGrid.css b/xos/core/xoslib/static/css/xosServiceGrid.css
index 717197d..8264530 100644
--- a/xos/core/xoslib/static/css/xosServiceGrid.css
+++ b/xos/core/xoslib/static/css/xosServiceGrid.css
@@ -1,2 +1,2 @@
-xos-side-panel .xos-side-panel-content{position:fixed;width:30%;height:100%;padding:25px;background:#fff;overflow:scroll}xos-side-panel .xos-side-panel-content.right{border-left:1px solid #555;top:0;right:0;visibility:hidden;box-shadow:-10px 0 20px -8px rgba(0,0,0,.75)}xos-side-panel .xos-side-panel-content.right.out{animation:.5s slideOutRight ease-in-out;visibility:visible}xos-side-panel .xos-side-panel-content.right.in{animation:.5s slideInRight ease-in-out;visibility:visible}@keyframes slideInRight{from{transform:translate3d(100%,0,0);visibility:visible}to{transform:translate3d(0,0,0)}}@keyframes slideOutRight{from{transform:translate3d(0,0,0)}to{visibility:hidden;transform:translate3d(100%,0,0)}}#xosServiceGrid service-graph{display:block;width:100%;height:600px}#xosServiceGrid .node{stroke:#337ab7;fill:white}#xosServiceGrid .node.xos{fill:#d9534f}#xosServiceGrid .link{stroke:black;stroke-width:2px}
+xos-side-panel .xos-side-panel-content{position:fixed;width:30%;height:100%;padding:25px;background:#fff;overflow:scroll}xos-side-panel .xos-side-panel-content.right{border-left:1px solid #555;top:0;right:0;visibility:hidden;box-shadow:-10px 0 20px -8px rgba(0,0,0,.75)}xos-side-panel .xos-side-panel-content.right.out{animation:.5s slideOutRight ease-in-out;visibility:visible}xos-side-panel .xos-side-panel-content.right.in{animation:.5s slideInRight ease-in-out;visibility:visible}@keyframes slideInRight{from{transform:translate3d(100%,0,0);visibility:visible}to{transform:translate3d(0,0,0)}}@keyframes slideOutRight{from{transform:translate3d(0,0,0)}to{visibility:hidden;transform:translate3d(100%,0,0)}}#xosServiceGrid{width:100%}#xosServiceGrid service-graph{display:block;width:100%;height:600px}#xosServiceGrid .node{stroke:#337ab7;fill:white}#xosServiceGrid .node.xos{fill:#d9534f}#xosServiceGrid .link{stroke:#286090;stroke-width:2px}#xosServiceGrid .link-arrow{stroke:#286090;fill:#286090}
xos-side-panel .xos-side-panel-content{position:fixed;width:30%;height:100%;padding:25px;background:#fff;overflow:scroll}xos-side-panel .xos-side-panel-content.right{border-left:1px solid #555;top:0;right:0;visibility:hidden;box-shadow:-10px 0 20px -8px rgba(0,0,0,.75)}xos-side-panel .xos-side-panel-content.right.out{animation:.5s slideOutRight ease-in-out;visibility:visible}xos-side-panel .xos-side-panel-content.right.in{animation:.5s slideInRight ease-in-out;visibility:visible}@keyframes slideInRight{from{transform:translate3d(100%,0,0);visibility:visible}to{transform:translate3d(0,0,0)}}@keyframes slideOutRight{from{transform:translate3d(0,0,0)}to{visibility:hidden;transform:translate3d(100%,0,0)}}
\ No newline at end of file
diff --git a/xos/core/xoslib/static/js/xosServiceGrid.js b/xos/core/xoslib/static/js/xosServiceGrid.js
index 43d1b98..5a6f608 100644
--- a/xos/core/xoslib/static/js/xosServiceGrid.js
+++ b/xos/core/xoslib/static/js/xosServiceGrid.js
@@ -1 +1 @@
-"use strict";angular.module("xos.serviceGrid",["ngResource","ngCookies","ui.router","xos.helpers"]).config(["$stateProvider",function(e){e.state("serviceGrid",{url:"/",template:"<service-grid></service-grid>"}).state("serviceGraph",{url:"/graph",template:"<service-graph></service-graph>"})}]).config(["$httpProvider",function(e){e.interceptors.push("NoHyperlinks")}]).directive("serviceGrid",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/service-grid.tpl.html",controller:["Services","ToscaEncoder","_",function(e,t,n){var r=this;this.tableConfig={columns:[{label:"Status",prop:"status",type:"icon",formatter:function(e){var t=parseInt(e.backend_status.match(/^[0-9]/)[0]);switch(t){case 0:return"time";case 1:return"ok";case 2:return"remove"}}},{label:"Name",prop:"name",link:function(e){return""+e.view_url.replace(/\$[a-z]+\$/,e.id)}},{label:"Kind",prop:"kind"},{label:"Enabled",prop:"enabled",type:"boolean"}],filter:"field",order:{field:"name"},actions:[{label:"export",icon:"export",cb:function(e){r.tosca="",t.serviceToTosca(e).then(function(e){r.showFeedback=!0,r.tosca=e})}}]},e.query().$promise.then(function(e){r.services=n.map(e,function(e){return e.status=0!==parseInt(e.backend_status.match(/^[0-9]/)[0]),e})})["catch"](function(e){throw new Error(e)})}]}}),function(){angular.module("xos.serviceGrid").service("ToscaEncoder",["$q","_","ArchiveManager","ServiceEncoder","SlicesEncoder",function(e,t,n,r,o){var i=this,a={tosca_definitions_version:"tosca_simple_yaml_1_0",description:"",imports:["custom_types/xos.yaml"],topology_template:{node_templates:{}}};this.toYml=function(e){return jsyaml.dump(e).replace(/'/g,"")},this["export"]=function(e){n.download(e.name);var t=i.toYml(a);return t},this.serviceToTosca=function(t){n.createArchive(),a.topology_template.node_templates={},a.description="Just enough Tosca to get the "+t.humanReadableName+" service up and running";var s=e.defer();return r.formatServiceProperties(t,a).then(function(e){return o.getServiceSlices(t,e)}).then(function(e){return r.getServiceRequirements(t,e)}).then(function(e){n.addFile(t.name+"_service.yaml",i.toYml(e)),i["export"](t),s.resolve(i.toYml(e))})["catch"](function(e){s.reject(e)}),s.promise}}])}(),angular.module("xos.serviceGrid").run(["$templateCache",function(e){e.put("templates/service-graph.tpl.html",'<div class="row">\n <div class="col-sm-10">\n <h1>Graph</h1>\n </div>\n <div class="col-sm-2">\n <a href="/admin/core/service/add" class="btn btn-success btn-block">\n <i class="glyphicon glyphicon-plus"></i>\n Add Service\n </a>\n <a href="#/" class="btn btn-default btn-block">\n Service List\n </a>\n </div>\n</div>\n<xos-side-panel config="vm.panelConfig" show="vm.panelShow">\n <h1>\n {{vm.selectedNode.name}}\n <small>\n <i class="glyphicon glyphicon-{{vm.selectedNode.icon}}"></i>\n </small>\n </h1>\n <table class="table">\n <tr>\n <td>Kind:</td>\n <td>{{vm.selectedNode.kind}}</td>\n </tr>\n <tr>\n <td>Enabled:</td>\n <td>{{vm.selectedNode.enabled}}</td>\n </tr>\n <tr>\n <td>Status:</td>\n <td>{{vm.selectedNode.backend_status}}</td>\n </tr>\n </table>\n <a ng-click="vm.exportToTosca(vm.selectedNode)" class="btn btn-primary">\n Export to TOSCA\n <i class="glyphicon glyphicon-export"></i>\n </a>\n</xos-side-panel>'),e.put("templates/service-grid.tpl.html",'<div class="row">\n <div class="col-md-10 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.services"></xos-table>\n </div>\n <div class="col-md-2">\n <a href="/admin/core/service/add" class="btn btn-success btn-block">\n <i class="glyphicon glyphicon-plus"></i>\n Add Service\n </a>\n <a href="#/graph" class="btn btn-default btn-block">\n Tenancy Graph\n </a>\n </div>\n</div>\n\n<div class="row">\n <div class="col-xs-12">\n <div class="alert alert-info" ng-show="vm.showFeedback">\n Remember that you should copy any key artifact inside the container in <pre>/opt/xos/tosca</pre>!\n </div>\n </div>\n</div>\n\n<pre ng-show="vm.tosca">\n{{vm.tosca}}\n</pre>')}]);var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("SlicesEncoder",["$q","_","Slices","SiteEncoder","ImageEncoder","NetworkEncoder",function(e,t,n,r,o,i){var a=this;this.buildTosca=function(n,a,s){var c={},l=e.defer();return n=t.reduce(n,function(e,t){if(e[t.name]={type:"tosca.nodes.Slice",properties:{network:t.network},requirements:[{management:{node:"management",relationship:"tosca.relationships.ConnectsToNetwork"}}]},angular.isDefined(s)){var n={};n[s+"_service"]={node:"service#"+s,relationship:"tosca.relationships.MemberOfService"},e[t.name].requirements.push(n)}return angular.isDefined(t.description)&&(e[t.name].description=t.description),angular.isDefined(t.site)&&(c[t.name+"#site"]=r.buildTosca(t.site,a)),angular.isDefined(t.default_image)&&(c[t.name+"#image"]=o.buildTosca(t.default_image,a)),angular.isDefined(t.networks)&&t.networks.length>0&&(c[t.name+"#management"]=i.getSliceNetworks(t,a)),e},{}),Object.keys(c).length>0?!function(){var t={site:"tosca.relationships.MemberOfSite",image:"tosca.relationships.DefaultImage"};a.topology_template.node_templates.management={type:"tosca.nodes.network.Network.XOS",properties:{"no-create":!0,"no-delete":!0,"no-update":!0}},e.all(c).then(function(e){var r=!0,o=!1,i=void 0;try{for(var s,c=Object.keys(e)[Symbol.iterator]();!(r=(s=c.next()).done);r=!0){var u=s.value,d=u.split("#"),p=_slicedToArray(d,2),v=p[0],m=p[1];if(angular.isDefined(t[m])){n[v].requirements||(n[v].requirements=[]);var f=_slicedToArray(e[u],2),h=f[0],g=f[1],y={},b=void 0;b="site"===m?g.name:m+"#"+g.name,y[m]={node:b,relationship:t[m]},n[v].requirements.push(y),angular.extend(a,h)}}}catch(_){o=!0,i=_}finally{try{!r&&c["return"]&&c["return"]()}finally{if(o)throw i}}angular.extend(a.topology_template.node_templates,n),l.resolve(a)})["catch"](function(e){throw new Error(e)})}():(angular.extend(a.topology_template.node_templates,n),l.resolve(a)),l.promise},this.getServiceSlices=function(t,r){var o=e.defer();return n.query({service:t.id}).$promise.then(function(e){return a.buildTosca(e,r,t.name)}).then(function(e){o.resolve(e)}),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("SiteEncoder",["$q","Sites",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t[""+e.name]={type:"tosca.nodes.Site"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](o.reject),o.promise}}])}(),function(){angular.module("xos.uiComponents").directive("xosSidePanel",function(){return{restrict:"E",scope:{config:"=",show:"="},template:'\n <div class="xos-side-panel-content {{vm.classes.join(\' \')}}">\n <div class="row">\n <div class="col-xs-12">\n <button type="button" class="close" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12" ng-transclude></div>\n </div>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$scope","$timeout","_",function(e,t,n){var r=this;console.log(this.show),this.classes=[],this.classes.push(this.config.position),this.dismiss=function(){r.show=!1,r.classes=r.toggleClass(r.classes),t(function(){return n.remove(r.classes,function(e){return"out"===e})},500)},this.toggleClass=function(e){return e.indexOf("in")>-1?(n.remove(r.classes,function(e){return"in"===e}),r.classes.push("out"),e):(n.remove(r.classes,function(e){return"out"===e}),r.classes.push("in"),e)},e.$watch(function(){return r.show},function(e){angular.isDefined(e)&&e&&e===!0&&(r.classes=r.toggleClass(r.classes))})}]}})}(),function(){angular.module("xos.serviceGrid").service("ServiceEncoder",["$q","ArchiveManager","Tenants","Services",function(e,t,n,r){var o={fabric:"tosca.nodes.FabricService",onos:"tosca.nodes.ONOSService",vCPE:"tosca.nodes.VSGService",vOLT:"tosca.nodes.VOLTService",vROUTER:"tosca.nodes.VRouterService",VTN:"tosca.nodes.VTNService",vTR:"tosca.nodes.Service"};this.formatServiceProperties=function(n,r){var i=e.defer(),a="service#"+n.name;r.topology_template.node_templates[a]={},r.topology_template.node_templates[a].type=o[n.kind]||"tosca.nodes.Service";var s={properties:{kind:n.kind}};return angular.isDefined(n.view_url)&&(s.properties.view_url=n.view_url),angular.isDefined(n.icon_url)&&(s.properties.icon_url=n.icon_url),angular.isDefined(n.private_key_fn)&&(s.properties.private_key_fn=n.private_key_fn),angular.isDefined(n.public_key)&&(t.addFile(n.name+"_rsa.pub",n.public_key),s.properties.public_key="{ get_artifact: [ SELF, pubkey, LOCAL_FILE] }",s.artifacts={pubkey:"/opt/xos/tosca/"+n.name+"/"+n.name+"_rsa.pub"},r.topology_template.node_templates[a].artifacts=s.artifacts),r.topology_template.node_templates[a].properties=s.properties,i.resolve(r),i.promise},this.getServiceRequirements=function(t,o){var i=e.defer();return n.query({subscriber_service:t.id}).$promise.then(function(t){var n=[];return t=_.uniqBy(t,"provider_service"),_.forEach(t,function(e){n.push(r.get({id:e.provider_service}).$promise)}),e.all(n)}).then(function(e){var n=_.reduce(e,function(e,t){return e.concat(t.name)},[]);if(n=_.reduce(n,function(e,t){var n=t+"_tenant",r={};return r[n]={node:"service#"+t,relationship:"tosca.relationships.TenantOfService"},e.concat(r)},[]),n.length>0){_.forEach(n,function(e){var t=e[Object.keys(e)[0]].node;o.topology_template.node_templates[t]={type:"tosca.nodes.Service",properties:{"no-create":!0,"no-delete":!0,"no-update":!0}}});var r="service#"+t.name;o.topology_template.node_templates[r].requirements=n}i.resolve(o)}),i.promise}}])}();var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("Graph",["$q","Tenants","Services","Subscribers",function(e,t,n,r){var o=new graphlib.Graph,i=!1,a=function(){var i=e.defer();return e.all([t.query().$promise,n.query().$promise,r.query().$promise]).then(function(e){var t=_slicedToArray(e,3),n=t[0],r=t[1];t[2];r.forEach(function(e){return o.setNode(e.id,angular.extend(e,{type:"service"}))}),n.filter(function(e){return e.subscriber_service&&e.provider_service}).forEach(function(e){return o.setEdge(e.subscriber_service,e.provider_service,e,e.name)}),i.resolve(o)}),i.promise};this.getGraph=function(){var t=e.defer();return i?t.resolve(o):a().then(function(e){i=!0,t.resolve(e)})["catch"](console.log),{$promise:t.promise}}}]).directive("serviceGraph",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/service-graph.tpl.html",controller:["$scope","$element","GraphService","Graph","ToscaEncoder",function(e,t,n,r,o){var i=void 0,a=t[0],s=void 0,c=void 0,l=this;this.panelConfig={position:"right"};var u=function(e){s.attr("cx",function(e){return e.x}).attr("cy",function(e){return e.y}).attr({transform:function(e){return"translate("+e.x+", "+e.y+")"}}),c.attr("x1",function(e){return e.source.x}).attr("y1",function(e){return e.source.y}).attr("x2",function(e){return v(e)[0]}).attr("y2",function(e){return v(e)[1]})};r.getGraph().$promise.then(function(t){var n=t.edges().map(function(e){return{source:t.node(e.v),target:t.node(e.w)}}),r=t.nodes().map(function(e){return t.node(e)});r.push({name:"XOS",type:"xos",x:a.clientWidth/2,y:a.clientHeight/2,fixed:!0}),d(a),p();var o=d3.layout.force().nodes(r).links(n).charge(-1060).gravity(.1).linkDistance(200).size([a.clientWidth,a.clientHeight]).on("tick",u).start();c=i.selectAll(".link").data(n).enter().insert("line").attr("class","link").attr("marker-end","url(#arrow)"),s=i.selectAll(".node").data(r).enter().append("g").call(o.drag).on("mousedown",function(t){e.$apply(function(){if("XOS"!==t.name){l.panelShow=!0;var e=parseInt(t.backend_status.match(/^[0-9]/)[0]);switch(console.log(e),e){case 0:t.icon="time";break;case 1:t.icon="ok";break;case 2:t.icon="remove"}l.selectedNode=t}}),d3.event.stopPropagation()}),s.append("circle").attr({"class":function(e){return"node "+(e.type||"")},r:10}),s.append("text").attr({"text-anchor":"middle","alignment-baseline":"middle"}).text(function(e){return e.humanReadableName||e.name}),s.select("circle").attr({r:function(e){var t=d3.select(this).node().parentNode,n=d3.select(t).select("text").node().getBBox(),r=n.width/2+10;return e.nodeWidth=2*r,r}})});var d=function(e){d3.select(e).select("svg").remove(),i=d3.select(e).append("svg").style("width",e.clientWidth+"px").style("height",e.clientHeight+"px")},p=function(){i.append("svg:defs").selectAll("marker").data(["arrow"]).enter().append("svg:marker").attr("id",String).attr("viewBox","0 -5 10 10").attr("refX",10).attr("refY",0).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto").append("svg:path").attr("d","M0,-5L10,0L0,5")},v=function(e){var t=e.target.nodeWidth/2,n=e.target.x-e.source.x,r=e.target.y-e.source.y,o=Math.atan2(r,n),i=e.target.x-Math.cos(o)*t,a=e.target.y-Math.sin(o)*t;return[i,a]};this.exportToTosca=function(e){o.serviceToTosca(e)}}]}})}();var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("NetworkEncoder",["$q","Networks","NetworkTemplateEncoder",function(e,t,n){var r=this;this.buildTosca=function(t,r){var o=angular.copy(t),i={},a=e.defer();try{t=_.reduce(t,function(e,t){return e["network#"+t.name]={type:"tosca.nodes.network.Network.XOS",requirements:[]},angular.isDefined(t.slices)&&(_.forEach(t.slices,function(n){var r={owner:{node:n.name,relationship:"tosca.relationships.MemberOfSlice"}},o={connection:{node:n.name,relationship:"tosca.relationships.ConnectsToSlice"}};e["network#"+t.name].requirements.push(r,o)}),angular.isDefined(t.template)&&(i[t.name]=n.buildTosca(t.template,r))),e},{}),Object.keys(i).length>0?e.all(i).then(function(e){if(e){var n=!0,i=!1,s=void 0;try{for(var c,l=Object.keys(e)[Symbol.iterator]();!(n=(c=l.next()).done);n=!0){var u=c.value,d=_slicedToArray(e[u],2),p=d[0],v=d[1];t["network#"+u].requirements.push({network_template:{node:"network_template#"+v.name,relationship:"tosca.relationships.UsesNetworkTemplate"}}),angular.extend(r,p)}}catch(m){i=!0,s=m}finally{try{!n&&l["return"]&&l["return"]()}finally{if(i)throw s}}}angular.extend(r.topology_template.node_templates,t),a.resolve([r,o])})["catch"](function(e){throw new Error(e)}):(angular.extend(r.topology_template.node_templates,t),a.resolve([r,o]))}catch(s){a.reject(s)}return a.promise},this.getSliceNetworks=function(n,o){var i=e.defer();return t.query({owner:n.id}).$promise.then(function(e){e=_.filter(e,function(e){return-1!==n.networks.indexOf(e.id)}),e=e.map(function(e){var t=e.slices.indexOf(n.id);return e.slices[t]=n,e}),r.buildTosca(e,o).then(i.resolve)["catch"](i.reject)}),i.promise}}])}(),function(){angular.module("xos.serviceGrid").service("NetworkTemplateEncoder",["$q","Networkstemplates",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t["network_template#"+e.name]={type:"tosca.nodes.NetworkTemplate"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](function(e){o.reject(e)}),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("ImageEncoder",["$q","Images",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t["image#"+e.name]={type:"tosca.nodes.Image"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](o.reject),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("ArchiveManager",function(){var e=this;this.createArchive=function(){e.archive=new JSZip},this.addFile=function(t,n){e.archive.file(t,n)},this.download=function(t){console.log(e.archive),e.archive.generateAsync({type:"blob"}).then(function(e){saveAs(e,t+".zip")})["catch"](function(e){console.log(e)})}})}(),angular.module("xos.serviceGrid").run(["$location",function(e){e.path("/")}]);
\ No newline at end of file
+"use strict";angular.module("xos.serviceGrid",["ngResource","ngCookies","ui.router","xos.helpers"]).config(["$stateProvider",function(e){e.state("serviceGrid",{url:"/",template:"<service-grid></service-grid>"}).state("serviceGraph",{url:"/graph",template:"<service-graph></service-graph>"})}]).config(["$httpProvider",function(e){e.interceptors.push("NoHyperlinks")}]).directive("serviceGrid",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/service-grid.tpl.html",controller:["Services","ToscaEncoder","_",function(e,t,n){var r=this;this.tableConfig={columns:[{label:"Status",prop:"status",type:"icon",formatter:function(e){var t=parseInt(e.backend_status.match(/^[0-9]/)[0]);switch(t){case 0:return"time";case 1:return"ok";case 2:return"remove"}}},{label:"Name",prop:"name",link:function(e){return""+e.view_url.replace(/\$[a-z]+\$/,e.id)}},{label:"Kind",prop:"kind"},{label:"Enabled",prop:"enabled",type:"boolean"}],filter:"field",order:{field:"name"},actions:[{label:"export",icon:"export",cb:function(e){r.tosca="",t.serviceToTosca(e).then(function(e){r.showFeedback=!0,r.tosca=e})}}]},e.query().$promise.then(function(e){r.services=n.map(e,function(e){return e.status=0!==parseInt(e.backend_status.match(/^[0-9]/)[0]),e})})["catch"](function(e){throw new Error(e)})}]}}),function(){angular.module("xos.serviceGrid").service("ToscaEncoder",["$q","_","ArchiveManager","ServiceEncoder","SlicesEncoder",function(e,t,n,r,o){var i=this,a={tosca_definitions_version:"tosca_simple_yaml_1_0",description:"",imports:["custom_types/xos.yaml"],topology_template:{node_templates:{}}};this.toYml=function(e){return jsyaml.dump(e).replace(/'/g,"")},this["export"]=function(e){n.download(e.name);var t=i.toYml(a);return t},this.serviceToTosca=function(t){n.createArchive(),a.topology_template.node_templates={},a.description="Just enough Tosca to get the "+t.humanReadableName+" service up and running";var s=e.defer();return r.formatServiceProperties(t,a).then(function(e){return o.getServiceSlices(t,e)}).then(function(e){return r.getServiceRequirements(t,e)}).then(function(e){n.addFile(t.name+"_service.yaml",i.toYml(e)),i["export"](t),s.resolve(i.toYml(e))})["catch"](function(e){s.reject(e)}),s.promise}}])}(),angular.module("xos.serviceGrid").run(["$templateCache",function(e){e.put("templates/service-graph.tpl.html",'<div class="row">\n <div class="col-sm-10">\n <h1>Graph</h1>\n </div>\n <div class="col-sm-2">\n <a href="/admin/core/service/add" class="btn btn-success btn-block">\n <i class="glyphicon glyphicon-plus"></i>\n Add Service\n </a>\n <a href="#/" class="btn btn-default btn-block">\n Service List\n </a>\n </div>\n</div>\n<xos-side-panel config="vm.panelConfig" show="vm.panelShow">\n <h1>\n {{vm.selectedNode.name}}\n <small>\n <i class="glyphicon glyphicon-{{vm.selectedNode.icon}}"></i>\n </small>\n </h1>\n <table class="table">\n <tr>\n <td>Kind:</td>\n <td>{{vm.selectedNode.kind}}</td>\n </tr>\n <tr>\n <td>Enabled:</td>\n <td>{{vm.selectedNode.enabled}}</td>\n </tr>\n <tr>\n <td>Status:</td>\n <td>{{vm.selectedNode.backend_status}}</td>\n </tr>\n </table>\n <a ng-click="vm.exportToTosca(vm.selectedNode)" class="btn btn-primary">\n Export to TOSCA\n <i class="glyphicon glyphicon-export"></i>\n </a>\n</xos-side-panel>'),e.put("templates/service-grid.tpl.html",'<div class="row">\n <div class="col-md-10 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.services"></xos-table>\n </div>\n <div class="col-md-2">\n <a href="/admin/core/service/add" class="btn btn-success btn-block">\n <i class="glyphicon glyphicon-plus"></i>\n Add Service\n </a>\n <a href="#/graph" class="btn btn-default btn-block">\n Tenancy Graph\n </a>\n </div>\n</div>\n\n<div class="row">\n <div class="col-xs-12">\n <div class="alert alert-info" ng-show="vm.showFeedback">\n Remember that you should copy any key artifact inside the container in <pre>/opt/xos/tosca</pre>!\n </div>\n </div>\n</div>\n\n<pre ng-show="vm.tosca">\n{{vm.tosca}}\n</pre>'),e.put("templates/users-list.tpl.html",'<xos-table config="vm.tableConfig" data="vm.users"></xos-table>')}]);var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("SlicesEncoder",["$q","_","Slices","SiteEncoder","ImageEncoder","NetworkEncoder",function(e,t,n,r,o,i){var a=this;this.buildTosca=function(n,a,s){var c={},l=e.defer();return n=t.reduce(n,function(e,t){if(e[t.name]={type:"tosca.nodes.Slice",properties:{network:t.network},requirements:[{management:{node:"management",relationship:"tosca.relationships.ConnectsToNetwork"}}]},angular.isDefined(s)){var n={};n[s+"_service"]={node:"service#"+s,relationship:"tosca.relationships.MemberOfService"},e[t.name].requirements.push(n)}return angular.isDefined(t.description)&&(e[t.name].description=t.description),angular.isDefined(t.site)&&(c[t.name+"#site"]=r.buildTosca(t.site,a)),angular.isDefined(t.default_image)&&(c[t.name+"#image"]=o.buildTosca(t.default_image,a)),angular.isDefined(t.networks)&&t.networks.length>0&&(c[t.name+"#management"]=i.getSliceNetworks(t,a)),e},{}),Object.keys(c).length>0?!function(){var t={site:"tosca.relationships.MemberOfSite",image:"tosca.relationships.DefaultImage"};a.topology_template.node_templates.management={type:"tosca.nodes.network.Network.XOS",properties:{"no-create":!0,"no-delete":!0,"no-update":!0}},e.all(c).then(function(e){var r=!0,o=!1,i=void 0;try{for(var s,c=Object.keys(e)[Symbol.iterator]();!(r=(s=c.next()).done);r=!0){var u=s.value,d=u.split("#"),p=_slicedToArray(d,2),v=p[0],m=p[1];if(angular.isDefined(t[m])){n[v].requirements||(n[v].requirements=[]);var f=_slicedToArray(e[u],2),h=f[0],g=f[1],y={},b=void 0;b="site"===m?g.name:m+"#"+g.name,y[m]={node:b,relationship:t[m]},n[v].requirements.push(y),angular.extend(a,h)}}}catch(_){o=!0,i=_}finally{try{!r&&c["return"]&&c["return"]()}finally{if(o)throw i}}angular.extend(a.topology_template.node_templates,n),l.resolve(a)})["catch"](function(e){throw new Error(e)})}():(angular.extend(a.topology_template.node_templates,n),l.resolve(a)),l.promise},this.getServiceSlices=function(t,r){var o=e.defer();return n.query({service:t.id}).$promise.then(function(e){return a.buildTosca(e,r,t.name)}).then(function(e){o.resolve(e)}),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("SiteEncoder",["$q","Sites",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t[""+e.name]={type:"tosca.nodes.Site"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](o.reject),o.promise}}])}(),function(){angular.module("xos.uiComponents").directive("xosSidePanel",function(){return{restrict:"E",scope:{config:"=",show:"="},template:'\n <div class="xos-side-panel-content {{vm.classes.join(\' \')}}">\n <div class="row">\n <div class="col-xs-12">\n <button type="button" class="close" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12" ng-transclude></div>\n </div>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$scope","$timeout","_",function(e,t,n){var r=this;console.log(this.show),this.classes=[],this.classes.push(this.config.position),this.dismiss=function(){r.show=!1,r.classes=r.toggleClass(r.classes),t(function(){return n.remove(r.classes,function(e){return"out"===e})},500)},this.toggleClass=function(e){return e.indexOf("in")>-1?(n.remove(r.classes,function(e){return"in"===e}),r.classes.push("out"),e):(n.remove(r.classes,function(e){return"out"===e}),r.classes.push("in"),e)},e.$watch(function(){return r.show},function(e){angular.isDefined(e)&&e&&e===!0&&(r.classes=r.toggleClass(r.classes))})}]}})}(),function(){angular.module("xos.serviceGrid").service("ServiceEncoder",["$q","ArchiveManager","Tenants","Services",function(e,t,n,r){var o={fabric:"tosca.nodes.FabricService",onos:"tosca.nodes.ONOSService",vCPE:"tosca.nodes.VSGService",vOLT:"tosca.nodes.VOLTService",vROUTER:"tosca.nodes.VRouterService",VTN:"tosca.nodes.VTNService",vTR:"tosca.nodes.Service"};this.formatServiceProperties=function(n,r){var i=e.defer(),a="service#"+n.name;r.topology_template.node_templates[a]={},r.topology_template.node_templates[a].type=o[n.kind]||"tosca.nodes.Service";var s={properties:{kind:n.kind}};return angular.isDefined(n.view_url)&&(s.properties.view_url=n.view_url),angular.isDefined(n.icon_url)&&(s.properties.icon_url=n.icon_url),angular.isDefined(n.private_key_fn)&&(s.properties.private_key_fn=n.private_key_fn),angular.isDefined(n.public_key)&&(t.addFile(n.name+"_rsa.pub",n.public_key),s.properties.public_key="{ get_artifact: [ SELF, pubkey, LOCAL_FILE] }",s.artifacts={pubkey:"/opt/xos/tosca/"+n.name+"/"+n.name+"_rsa.pub"},r.topology_template.node_templates[a].artifacts=s.artifacts),r.topology_template.node_templates[a].properties=s.properties,i.resolve(r),i.promise},this.getServiceRequirements=function(t,o){var i=e.defer();return n.query({subscriber_service:t.id}).$promise.then(function(t){var n=[];return t=_.uniqBy(t,"provider_service"),_.forEach(t,function(e){n.push(r.get({id:e.provider_service}).$promise)}),e.all(n)}).then(function(e){var n=_.reduce(e,function(e,t){return e.concat(t.name)},[]);if(n=_.reduce(n,function(e,t){var n=t+"_tenant",r={};return r[n]={node:"service#"+t,relationship:"tosca.relationships.TenantOfService"},e.concat(r)},[]),n.length>0){_.forEach(n,function(e){var t=e[Object.keys(e)[0]].node;o.topology_template.node_templates[t]={type:"tosca.nodes.Service",properties:{"no-create":!0,"no-delete":!0,"no-update":!0}}});var r="service#"+t.name;o.topology_template.node_templates[r].requirements=n}i.resolve(o)}),i.promise}}])}();var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("Graph",["$q","Tenants","Services","Subscribers",function(e,t,n,r){var o=new graphlib.Graph,i=!1,a=function(){var i=e.defer();return e.all([t.query().$promise,n.query().$promise,r.query().$promise]).then(function(e){var t=_slicedToArray(e,3),n=t[0],r=t[1];t[2];r.forEach(function(e){return o.setNode(e.id,angular.extend(e,{type:"service"}))}),n.filter(function(e){return e.subscriber_service&&e.provider_service}).forEach(function(e){return o.setEdge(e.subscriber_service,e.provider_service,e,e.name)}),i.resolve(o)}),i.promise};this.getGraph=function(){var t=e.defer();return i?t.resolve(o):a().then(function(e){i=!0,t.resolve(e)})["catch"](console.log),{$promise:t.promise}}}]).directive("serviceGraph",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/service-graph.tpl.html",controller:["$scope","$element","GraphService","Graph","ToscaEncoder",function(e,t,n,r,o){var i=void 0,a=t[0],s=void 0,c=void 0,l=void 0,u=void 0,d=this;this.panelConfig={position:"right"};var p=function(e,t){return t.x>e.x?e.x+(t.x-e.x)/2:t.x+(e.x-t.x)/2},v=function(e,t){return t.y>e.y?Math.round(e.y+(t.y-e.y)/2):Math.round(t.y+(e.y-t.y)/2)},m=function(){s.attr("cx",function(e){return e.x}).attr("cy",function(e){return e.y}).attr({transform:function(e){return"translate("+e.x+", "+e.y+")"}}),i.selectAll(".link").attr("x1",function(e){return e.source.x}).attr("y1",function(e){return e.source.y}).attr("x2",function(e){return _(e)[0]}).attr("y2",function(e){return _(e)[1]}),i.selectAll(".link-text").attr("x",function(e){return p(e.source,{x:_(e)[0],y:_(e)[1]})}).attr("y",function(e){return v(e.source,{x:_(e)[0],y:_(e)[1]})}).attr("transform",function(e){var t=p(e.source,{x:_(e)[0],y:_(e)[1]}),n=v(e.source,{x:_(e)[0],y:_(e)[1]});return"rotate(-30, "+t+", "+n+")"})},f=1,h=1,g=function(e,t,n){var r=t.node(e),o=a.clientWidth/(n+1);if(t.nodeEdges(e).length>0){var i={y:a.clientHeight/4,x:o*f,fixed:!0};angular.extend(r,i),f+=1}else{var i={y:a.clientHeight/2+a.clientHeight/4,x:(o+o/2)*h,fixed:!0};angular.extend(r,i),h+=1}return r};r.getGraph().$promise.then(function(t){u=t.edges().map(function(e){return{source:t.node(e.v),target:t.node(e.w),tenant:t.edge(e)}});var n=graphlib.alg.components(t).reduce(function(e,t){return e.length>t.length?e:t}).length;c=t.nodes().reverse().map(function(e){return g(e,t,n)}),y(a),b();var r=d3.layout.force().nodes(c).links(u).charge(-1060).gravity(.1).linkDistance(200).size([a.clientWidth,a.clientHeight]).on("tick",m).start();l=i.selectAll(".link-container").data(u).enter().insert("g").attr("class","link-container"),l.insert("line").attr("class","link").attr("marker-end","url(#arrow)");i.selectAll(".link-container").data(r.links()).insert("text").attr({"class":"link-text","text-anchor":"start"}).text(function(e){return""+e.tenant.humanReadableName});s=i.selectAll(".node").data(c).enter().append("g").call(r.drag).on("mousedown",function(t){e.$apply(function(){if("XOS"!==t.name){d.panelShow=!0;var e=parseInt(t.backend_status.match(/^[0-9]/)[0]);switch(console.log(e),e){case 0:t.icon="time";break;case 1:t.icon="ok";break;case 2:t.icon="remove"}d.selectedNode=t}}),d3.event.stopPropagation()}),s.append("circle").attr({"class":function(e){return"node "+(e.type||"")},r:10}),s.append("text").attr({"text-anchor":"middle","alignment-baseline":"middle"}).text(function(e){return e.humanReadableName||e.name}),s.select("circle").attr({r:function(e){var t=d3.select(this).node().parentNode,n=d3.select(t).select("text").node().getBBox(),r=n.width/2+10;return e.nodeWidth=2*r,r}})});var y=function(e){d3.select(e).select("svg").remove(),i=d3.select(e).append("svg").style("width",e.clientWidth+"px").style("height",e.clientHeight+"px")},b=function(){i.append("svg:defs").selectAll("marker").data(["arrow"]).enter().append("svg:marker").attr("id",String).attr("viewBox","0 -5 10 10").attr("refX",10).attr("refY",0).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto").attr("class","link-arrow").append("svg:path").attr("d","M0,-5L10,0L0,5")},_=function(e){var t=e.target.nodeWidth/2,n=e.target.x-e.source.x,r=e.target.y-e.source.y,o=Math.atan2(r,n),i=e.target.x-Math.cos(o)*t,a=e.target.y-Math.sin(o)*t;return[i,a]};this.exportToTosca=function(e){o.serviceToTosca(e)}}]}})}();var _slicedToArray=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&s["return"]&&s["return"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();!function(){angular.module("xos.serviceGrid").service("NetworkEncoder",["$q","Networks","NetworkTemplateEncoder",function(e,t,n){var r=this;this.buildTosca=function(t,r){var o=angular.copy(t),i={},a=e.defer();try{t=_.reduce(t,function(e,t){return e["network#"+t.name]={type:"tosca.nodes.network.Network.XOS",requirements:[]},angular.isDefined(t.slices)&&(_.forEach(t.slices,function(n){var r={owner:{node:n.name,relationship:"tosca.relationships.MemberOfSlice"}},o={connection:{node:n.name,relationship:"tosca.relationships.ConnectsToSlice"}};e["network#"+t.name].requirements.push(r,o)}),angular.isDefined(t.template)&&(i[t.name]=n.buildTosca(t.template,r))),e},{}),Object.keys(i).length>0?e.all(i).then(function(e){if(e){var n=!0,i=!1,s=void 0;try{for(var c,l=Object.keys(e)[Symbol.iterator]();!(n=(c=l.next()).done);n=!0){var u=c.value,d=_slicedToArray(e[u],2),p=d[0],v=d[1];t["network#"+u].requirements.push({network_template:{node:"network_template#"+v.name,relationship:"tosca.relationships.UsesNetworkTemplate"}}),angular.extend(r,p)}}catch(m){i=!0,s=m}finally{try{!n&&l["return"]&&l["return"]()}finally{if(i)throw s}}}angular.extend(r.topology_template.node_templates,t),a.resolve([r,o])})["catch"](function(e){throw new Error(e)}):(angular.extend(r.topology_template.node_templates,t),a.resolve([r,o]))}catch(s){a.reject(s)}return a.promise},this.getSliceNetworks=function(n,o){var i=e.defer();return t.query({owner:n.id}).$promise.then(function(e){e=_.filter(e,function(e){return-1!==n.networks.indexOf(e.id)}),e=e.map(function(e){var t=e.slices.indexOf(n.id);return e.slices[t]=n,e}),r.buildTosca(e,o).then(i.resolve)["catch"](i.reject)}),i.promise}}])}(),function(){angular.module("xos.serviceGrid").service("NetworkTemplateEncoder",["$q","Networkstemplates",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t["network_template#"+e.name]={type:"tosca.nodes.NetworkTemplate"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](function(e){o.reject(e)}),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("ImageEncoder",["$q","Images",function(e,t){this.buildTosca=function(n,r){var o=e.defer();return t.get({id:n}).$promise.then(function(e){var t={};t["image#"+e.name]={type:"tosca.nodes.Image"},angular.extend(r.topology_template.node_templates,t),o.resolve([r,e])})["catch"](o.reject),o.promise}}])}(),function(){angular.module("xos.serviceGrid").service("ArchiveManager",function(){var e=this;this.createArchive=function(){e.archive=new JSZip},this.addFile=function(t,n){e.archive.file(t,n)},this.download=function(t){console.log(e.archive),e.archive.generateAsync({type:"blob"}).then(function(e){saveAs(e,t+".zip")})["catch"](function(e){console.log(e)})}})}(),angular.module("xos.serviceGrid").run(["$location",function(e){e.path("/")}]);
\ No newline at end of file