Calculating rack details
diff --git a/views/ngXosViews/diagnostic/env/apt.js b/views/ngXosViews/diagnostic/env/apt.js
deleted file mode 100644
index 47c3e95..0000000
--- a/views/ngXosViews/diagnostic/env/apt.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// This is a default configuration for your development environment.
-// You can duplicate this configuration for any of your Backend Environments.
-// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
-// `NODE_ENV=local npm start`
-//
-// If xoscsrftoken or xossessionid are not specified the browser value are used
-// (works only for local environment as both application are served on the same domain)
-
-module.exports = {
-  host: 'http://apt045.apt.emulab.net:9999/',
-  xoscsrftoken: '56kxBxMtXeSJavUkqhgaAEXuSmi2snMy',
-  xossessionid: 'pffs71hhko88moqpa7mjxz1jrh9bklpb	'
-};
diff --git a/views/ngXosViews/diagnostic/env/default.js b/views/ngXosViews/diagnostic/env/default.js
index 956e8bb..f76b607 100644
--- a/views/ngXosViews/diagnostic/env/default.js
+++ b/views/ngXosViews/diagnostic/env/default.js
@@ -7,7 +7,7 @@
 // (works only for local environment as both application are served on the same domain)
 
 module.exports = {
-  host: 'http://clnode067.clemson.cloudlab.us:9999/',
-  xoscsrftoken: 'fPiSLn5CPCiTxWMtobv8g9bHS9y8pBYG',
-  xossessionid: 'tdhglvaav1tzemclbw0nc8k1olgiztgr'
+  host: 'http://clnode078.clemson.cloudlab.us:9999/',
+  xoscsrftoken: 'Lbrkulk7c9fQOloSjhQEqLdDDFRNHsuL',
+  xossessionid: '7j0w1m7t4qcyu472voe32jz6ck9dnq14'
 };
diff --git a/views/ngXosViews/diagnostic/env/srikanth.js b/views/ngXosViews/diagnostic/env/local.js
similarity index 76%
rename from views/ngXosViews/diagnostic/env/srikanth.js
rename to views/ngXosViews/diagnostic/env/local.js
index e79ab1d..d3076b7 100644
--- a/views/ngXosViews/diagnostic/env/srikanth.js
+++ b/views/ngXosViews/diagnostic/env/local.js
@@ -7,7 +7,7 @@
 // (works only for local environment as both application are served on the same domain)
 
 module.exports = {
-  host: 'http://130.127.133.41:9999',
-  xoscsrftoken: 'yxEhnmrR0GrRaAbTz3tiCEh1DzFDQ98v',
-  xossessionid: 'hgf01hgpsrw7g4mveuajuo6p7hezm1c5'
+  host: 'http://xos.local:9999/',
+  xoscsrftoken: 'IGSrPSAOmBorK8uxbbscQbn3ODPb9dDW',
+  xossessionid: 'i8hltbdt3po3uxkbvfmzy15o98p9s157'
 };
diff --git a/views/ngXosViews/diagnostic/spec/.eslintrc b/views/ngXosViews/diagnostic/spec/.eslintrc
new file mode 100644
index 0000000..c1764a5
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/.eslintrc
@@ -0,0 +1,15 @@
+{
+    "globals" :{
+        "describe": true,
+        "xdescribe": true,
+        "beforeEach": true,
+        "it": true,
+        "inject": true,
+        "expect": true,
+        "jasmine": true
+    },
+    "rules": {
+      "max-nested-callbacks": [0, 4],
+      "camelcase": 0
+    }
+}
diff --git a/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js b/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js
new file mode 100644
index 0000000..0353398
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js
@@ -0,0 +1,48 @@
+(function () {
+  'use strict';
+
+  describe('The Logic Topology Helper Service', () => {
+    
+    var Service;
+
+    beforeEach(module('xos.serviceTopology'));
+
+    // inject the rackHelper service
+    beforeEach(inject(function (_LogicTopologyHelper_) {
+      // The injector unwraps the underscores (_) from around the parameter names when matching
+      Service = _LogicTopologyHelper_;
+    }));
+
+    var customMatchers = {
+      toBeSimilar: () => {
+
+        const tolerance = 0.1;
+
+        return {
+          compare: (actual, expected) => {
+            return {
+              pass: (Math.abs(actual - expected) < tolerance),
+              message: `Expected ${actual} to be ${expected}`
+            }
+          }
+        }
+      }
+    };
+
+    beforeEach(function() {
+      jasmine.addMatchers(customMatchers);
+    });
+
+    it('should calculate horizontal position for each element', () => {
+      let [el0x, el1x, el2x, el3x, el4x, el5x] = Service.computeElementPosition(900);
+
+      expect(el0x).toBeSimilar(900 - 30);
+      expect(el1x).toBeSimilar(900 - 183.4);
+      expect(el2x).toBeSimilar(900 - 337.3);
+      expect(el3x).toBeSimilar(900 - 490.7);
+      expect(el4x).toBeSimilar(900 - 602.1);
+      expect(el5x).toBeSimilar(900 - 713.5);
+    });
+  });
+
+})();
diff --git a/views/ngXosViews/diagnostic/spec/rackHelper.test.js b/views/ngXosViews/diagnostic/spec/rackHelper.test.js
new file mode 100644
index 0000000..932b17f
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/rackHelper.test.js
@@ -0,0 +1,117 @@
+(function () {
+  'use strict';
+
+  const computeNodes = [
+    {
+      humanReadableName: 'cp-1.teone.xos-pg0.clemson.cloudlab.us',
+      instances: [
+        {
+          instance_name: 'mysite_clients-3'
+        },
+        {
+          instance_name: 'mysite_clients-4'
+        },
+        {
+          instance_name: 'mysite_clients-5'
+        }
+      ]
+    },
+    {
+      humanReadableName: 'cp-2.teone.xos-pg0.clemson.cloudlab.us',
+      instances: [
+        {
+          instance_name: 'mysite_clients-1'
+        },
+        {
+          instance_name: 'mysite_clients-2'
+        }
+      ]
+    },
+    {
+      humanReadableName: 'cp-2.teone.xos-pg0.clemson.cloudlab.us',
+      instances: [
+        {
+          instance_name: 'mysite_clients-1'
+        },
+        {
+          instance_name: 'mysite_clients-2'
+        }
+      ]
+    }
+  ];
+
+  describe('The Rack Helper Service', () => {
+    
+    var Service;
+
+    beforeEach(module('xos.serviceTopology'));
+
+    // inject the rackHelper service
+    beforeEach(inject(function (_RackHelper_) {
+      // The injector unwraps the underscores (_) from around the parameter names when matching
+      Service = _RackHelper_;
+    }));
+
+    describe('Given a list of instances', () => {
+      it('should calculate the Compute Node Size', () => {
+        const [width, height] = Service.getComputeNodeSize(computeNodes[0].instances);
+        expect(width).toBe(95);
+        expect(height).toBe(67);
+      });
+    });
+
+    describe('Given a list of Compute Nodes', () => {
+      it('should return rack size', () => {
+        const [width, height] = Service.getRackSize(computeNodes);
+        expect(width).toBe(105);
+        expect(height).toBe(179);
+      });
+    });
+
+    describe('Given an instance index', () => {
+      it('should return the position for first instance', () => {
+        const [x, y] = Service.getInstancePosition(0);
+        expect(x).toBe(5);
+        expect(y).toBe(25);
+      });
+
+      it('should return the position for second instance', () => {
+        const [x, y] = Service.getInstancePosition(1);
+        expect(x).toBe(50);
+        expect(y).toBe(25);
+      });
+
+      it('should return the position for third instance', () => {
+        const [x, y] = Service.getInstancePosition(2);
+        expect(x).toBe(5);
+        expect(y).toBe(46);
+      });
+
+      it('should return the position for 4th instance', () => {
+        const [x, y] = Service.getInstancePosition(3);
+        expect(x).toBe(50);
+        expect(y).toBe(46);
+      });
+    });
+
+    describe('Given an ComputeNode index', () => {
+      it('should return the position for 1st node', () => {
+        const [x, y] = Service.getComputeNodePosition(computeNodes, 0);
+        expect(x).toBe(5);
+        expect(y).toBe(5);
+      });
+
+      it('should return the position for 2st node', () => {
+        const [x, y] = Service.getComputeNodePosition(computeNodes, 1);
+        expect(x).toBe(5);
+        expect(y).toBe(77);
+      });
+
+      it('should return the position for 2st node', () => {
+        const [x, y] = Service.getComputeNodePosition(computeNodes, 2);
+        expect(x).toBe(5);
+        expect(y).toBe(128);
+      });
+    });
+  });
+})();
diff --git a/views/ngXosViews/diagnostic/spec/sample.test.js b/views/ngXosViews/diagnostic/spec/serviceChain.test.js
similarity index 100%
rename from views/ngXosViews/diagnostic/spec/sample.test.js
rename to views/ngXosViews/diagnostic/spec/serviceChain.test.js
diff --git a/views/ngXosViews/diagnostic/src/index.html b/views/ngXosViews/diagnostic/src/index.html
index afe559c..c3f0623 100644
--- a/views/ngXosViews/diagnostic/src/index.html
+++ b/views/ngXosViews/diagnostic/src/index.html
@@ -36,6 +36,7 @@
 <script src="/.tmp/serviceTopologyHelper.js"></script>
 <script src="/.tmp/serviceTopology.js"></script>
 <script src="/.tmp/rest_services.js"></script>
+<script src="/.tmp/rackHelper.js"></script>
 <script src="/.tmp/nodeDrawer.js"></script>
 <script src="/.tmp/logicTopologyHelper.js"></script>
 <script src="/.tmp/logicTopology.js"></script>
diff --git a/views/ngXosViews/diagnostic/src/js/config.js b/views/ngXosViews/diagnostic/src/js/config.js
index 99112b1..db1d832 100644
--- a/views/ngXosViews/diagnostic/src/js/config.js
+++ b/views/ngXosViews/diagnostic/src/js/config.js
@@ -6,6 +6,14 @@
     widthMargin: 20,
     heightMargin: 30,
     duration: 750,
+    // elWidths: { // this is the fixed width for elements
+    //   devices: 20,
+    //   subscribers: 20,
+    //   router: 20,
+    //   network: 104,
+    //   rack: 105
+    // },
+    elWidths: [20, 104, 105, 104, 20, 20],
     circle: {
       radius: 10,
       r: 10,
@@ -18,7 +26,7 @@
       y: -10
     },
     rack: {
-      width: 60,
+      width: 105,
       height: 50,
       x: -30,
       y: -25
@@ -26,12 +34,15 @@
     computeNode: {
       width: 50,
       height: 20,
+      margin: 5,
+      labelHeight: 10,
       x: -25,
       y: -10
     },
     instance: {
       width: 40,
       height: 16,
+      margin: 5,
       x: -20,
       y: -8
     }
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopology.js b/views/ngXosViews/diagnostic/src/js/logicTopology.js
index b689b89..afe61a8 100644
--- a/views/ngXosViews/diagnostic/src/js/logicTopology.js
+++ b/views/ngXosViews/diagnostic/src/js/logicTopology.js
@@ -16,8 +16,21 @@
 
         var svg;
 
+
+        const handleSvg = (el) => {
+
+          svg = d3.select(el)
+          .append('svg')
+          .style('width', `${el.clientWidth}px`)
+          .style('height', `${el.clientHeight}px`);
+        }
+
         $scope.$watch(() => this.subscribers, (subscribers) => {
           if(subscribers){
+
+            // TODO
+            // build here the full data structure
+
             LogicTopologyHelper.addSubscribers(svg, angular.copy(subscribers));
           }
         });
@@ -28,14 +41,6 @@
           }
         });
 
-        const handleSvg = (el) => {
-
-          svg = d3.select(el)
-          .append('svg')
-          .style('width', `${el.clientWidth}px`)
-          .style('height', `${el.clientHeight}px`);
-        }
-
         handleSvg($element[0]);
         LogicTopologyHelper.drawTree(svg);
       }
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
index d33de36..411da49 100644
--- a/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
+++ b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
@@ -31,6 +31,45 @@
     };
 
     /**
+     * Calculate the horizontal position for each element.
+     * subsrcribers, devices and routers have the same fixed width 20
+     * network have a fixed width 104
+     * rack have a fixed width 105
+     * build and array of 6 elements representing the position of each element in the svg
+     * to equally space them
+     */
+
+    this.computeElementPosition = (svgWidth) => {
+
+      let xPos = [];
+
+      let totalElWidth = lodash.reduce(serviceTopologyConfig.elWidths, (el, val) => val + el, 0);
+
+      let remainingSpace = svgWidth - totalElWidth - serviceTopologyConfig.widthMargin;
+
+      let step = remainingSpace / (serviceTopologyConfig.elWidths.length - 1);
+
+      lodash.forEach(serviceTopologyConfig.elWidths, (el, i) => {
+
+        // get half of the previous elements width
+        let previousElWidth = 0;
+        if(i !== 0){
+          previousElWidth = lodash.reduce(serviceTopologyConfig.elWidths.slice(0, i), (el, val) => val + el, 0) / 2;
+        }
+
+        let elPos =
+          serviceTopologyConfig.widthMargin // right margin
+          + (step * i) // space between elements
+          + (el / 2) // this el width
+          + previousElWidth; // previous elements width
+
+        xPos.push(svgWidth - elPos);
+      })
+
+      return xPos
+    };
+
+    /**
     * from a nested data structure,
     * create nodes and links for a D3 Tree Layout
     */
@@ -38,10 +77,12 @@
       let nodes = layout.nodes(data);
 
       // Normalize for fixed-depth.
-      nodes.forEach(function(d) {
+      nodes.forEach((d) => {
         // position the child node horizontally
-        const step = ((svgWidth - (serviceTopologyConfig.widthMargin * 2)) / 7);
-        d.y = (6 - d.depth) * step;
+        // const step = ((svgWidth - (serviceTopologyConfig.widthMargin * 2)) / 7);
+        // d.y = (6 - d.depth) * step;
+        d.y = this.computeElementPosition(svgWidth)[d.depth];
+        console.log(d.id, d.y);
       });
 
       let links = layout.links(nodes);
diff --git a/views/ngXosViews/diagnostic/src/js/rackHelper.js b/views/ngXosViews/diagnostic/src/js/rackHelper.js
new file mode 100644
index 0000000..037ecdd
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/rackHelper.js
@@ -0,0 +1,85 @@
+(function () {
+  angular.module('xos.serviceTopology')
+  .service('RackHelper', function(serviceTopologyConfig, lodash){
+
+    this.getComputeNodeLabelSize = () => {
+      return serviceTopologyConfig.computeNode.labelHeight + (serviceTopologyConfig.instance.margin * 2)
+    }
+
+    /**
+    * Given a list of instance should get the Compute Node size.
+    * They are placed in rows of 2 with 5px margin on each side.
+    */
+   
+    this.getComputeNodeSize = lodash.memoize((instances) => {
+      const width = (serviceTopologyConfig.instance.margin * 3) + (serviceTopologyConfig.instance.width *2);
+
+      const rows = Math.round(instances.length / 2);
+
+      const labelSpace = this.getComputeNodeLabelSize();
+
+      const height = (serviceTopologyConfig.instance.height * rows) + (serviceTopologyConfig.instance.margin * (rows + 1)) + labelSpace;
+      return [width, height];
+    });
+
+    /**
+    * Give a list on Compute Node should calculate the Rack Size.
+    * Compute nodes are placed in a single column with 5px margin on each side.
+    */
+    this.getRackSize = (nodes) => {
+
+      let width = 0;
+      let height = serviceTopologyConfig.computeNode.margin;
+
+      lodash.forEach(nodes, (node) => {
+        let [instanceWidth, instanceHeight] = this.getComputeNodeSize(node.instances);
+
+        width = instanceWidth + (serviceTopologyConfig.computeNode.margin * 2);
+        height += (instanceHeight + serviceTopologyConfig.computeNode.margin);
+      });
+
+      return [width, height];
+    };
+
+    /**
+    * Given an instance index, return the coordinates
+    */
+   
+    this.getInstancePosition = (position) => {
+      const row = Math.floor(position / 2);
+      const column = (position % 2) ? 1 : 0;
+
+      // add ComputeNode label size
+      const labelSpace = this.getComputeNodeLabelSize();
+
+      // x = margin + (width * column) + ( maring * column)
+      const x = serviceTopologyConfig.instance.margin + (serviceTopologyConfig.instance.width * column) + (serviceTopologyConfig.instance.margin * column);
+
+      // y = label + margin + (height * row) + ( maring * row)
+      const y = labelSpace + serviceTopologyConfig.instance.margin + (serviceTopologyConfig.instance.height * row) + (serviceTopologyConfig.instance.margin * row);
+      return [x, y];
+    };
+
+    /**
+    * Given an Compute Node index, return the coordinates
+    */
+
+    this.getComputeNodePosition = (nodes, position) => {
+      const x = serviceTopologyConfig.computeNode.margin;
+
+      let previousElEight = lodash.reduce(nodes.slice(0, position), (val, node) => {
+        return val + this.getComputeNodeSize(node.instances)[1]
+      }, 0);
+
+      console.log('previousElEight ' + previousElEight);
+      const y =
+        serviceTopologyConfig.computeNode.margin
+        + (serviceTopologyConfig.computeNode.margin * position)
+        + previousElEight;
+
+
+      return [x, y];
+    };
+
+  });
+})();
diff --git a/views/ngXosViews/diagnostic/src/js/rest_services.js b/views/ngXosViews/diagnostic/src/js/rest_services.js
index 87db711..8c3e317 100644
--- a/views/ngXosViews/diagnostic/src/js/rest_services.js
+++ b/views/ngXosViews/diagnostic/src/js/rest_services.js
@@ -39,7 +39,7 @@
                 node.instances = list[i];
                 return node;
               });
-
+              console.log(res.data);
               deferred.resolve(res.data);
             })