[CORD-1947] vEE OAM/CFM delay and jitter stat GUI

Change-Id: I82f364ed89805e9be7936adeefc49337917b57fd
diff --git a/xos/attic/header.py b/xos/attic/header.py
index 055ac8d..01106f4 100644
--- a/xos/attic/header.py
+++ b/xos/attic/header.py
@@ -20,3 +20,4 @@
 import traceback
 from xos.exceptions import *
 from xosconfig import Config
+
diff --git a/xos/vee.xproto b/xos/vee.xproto
index 6d268b8..62fc3de 100644
--- a/xos/vee.xproto
+++ b/xos/vee.xproto
@@ -12,4 +12,4 @@
 
      optional int32 s_tag = 1 [help_text = "s-tag", null = True, db_index = False, blank = True];
      optional int32 c_tag = 2 [help_text = "c-tag", null = True, db_index = False, blank = True];
-}
\ No newline at end of file
+}
diff --git a/xos/veestat/.dockerignore b/xos/veestat/.dockerignore
new file mode 100644
index 0000000..de807ff
--- /dev/null
+++ b/xos/veestat/.dockerignore
@@ -0,0 +1,3 @@
+node_modules
+typings
+npm-debug.log
\ No newline at end of file
diff --git a/xos/veestat/.gitignore b/xos/veestat/.gitignore
new file mode 100644
index 0000000..d8077e7
--- /dev/null
+++ b/xos/veestat/.gitignore
@@ -0,0 +1,7 @@
+node_modules/
+dist/
+.tmp/
+.idea/
+*.DS_Store
+typings/
+*.log
diff --git a/xos/veestat/Dockerfile b/xos/veestat/Dockerfile
new file mode 100644
index 0000000..d72a4af
--- /dev/null
+++ b/xos/veestat/Dockerfile
@@ -0,0 +1,49 @@
+# veestat
+FROM xosproject/xos-gui-extension-builder:candidate
+
+# Set environment vars
+ENV CODE_SOURCE .
+ENV CODE_DEST /var/www
+ENV VHOST /var/www/dist
+
+# Add the app deps
+COPY ${CODE_SOURCE}/package.json ${CODE_DEST}/package.json
+COPY ${CODE_SOURCE}/typings.json ${CODE_DEST}/typings.json
+
+# Install Deps
+WORKDIR ${CODE_DEST}
+RUN npm install
+RUN npm run typings
+
+# Build the app
+COPY ${CODE_SOURCE}/conf ${CODE_DEST}/conf
+COPY ${CODE_SOURCE}/gulp_tasks ${CODE_DEST}/gulp_tasks
+COPY ${CODE_SOURCE}/src ${CODE_DEST}/src
+COPY ${CODE_SOURCE}/gulpfile.js ${CODE_DEST}/gulpfile.js
+COPY ${CODE_SOURCE}/tsconfig.json ${CODE_DEST}/tsconfig.json
+COPY ${CODE_SOURCE}/tslint.json ${CODE_DEST}/tslint.json
+
+# Label image
+ARG org_label_schema_schema_version=1.0
+ARG org_label_schema_name=veestat-synchronizer
+ARG org_label_schema_version=unknown
+ARG org_label_schema_vcs_url=unknown
+ARG org_label_schema_vcs_ref=unknown
+ARG org_label_schema_build_date=unknown
+ARG org_opencord_vcs_commit_date=unknown
+ARG org_opencord_component_xos_gui_vcs_ref=unknown
+ARG org_opencord_component_xos_gui_vcs_url=unknown
+ARG org_opencord_component_xos_gui_version=unknown
+
+LABEL org.label-schema.schema-version=$org_label_schema_schema_version \
+      org.label-schema.name=$org_label_schema_name \
+      org.label-schema.version=$org_label_schema_version \
+      org.label-schema.vcs-url=$org_label_schema_vcs_url \
+      org.label-schema.vcs-ref=$org_label_schema_vcs_ref \
+      org.label-schema.build-date=$org_label_schema_build_date \
+      org.opencord.vcs-commit-date=$org_opencord_vcs_commit_date \
+      org.opencord.component.xos-gui.vcs-ref=$org_opencord_component_xos_gui_vcs_ref \
+      org.opencord.component.xos-gui.vcs-url=$org_opencord_component_xos_gui_vcs_url \
+      org.opencord.component.xos-gui.version=$org_opencord_component_xos_gui_version
+
+RUN npm run build
diff --git a/xos/veestat/README.md b/xos/veestat/README.md
new file mode 100644
index 0000000..f0afbe7
--- /dev/null
+++ b/xos/veestat/README.md
@@ -0,0 +1,3 @@
+# veestat
+
+This GUI extension displays the current and historical delay and jitter statistics by using the ONOS OAM/CFM application.
\ No newline at end of file
diff --git a/xos/veestat/conf/app/README.md b/xos/veestat/conf/app/README.md
new file mode 100755
index 0000000..6c45737
--- /dev/null
+++ b/xos/veestat/conf/app/README.md
@@ -0,0 +1,45 @@
+# XOS-GUI Config
+
+### Note: The configurations defined in this folder are for development only, they are most likely to be overridden by a volume mount defined in `service-profile`
+
+## App Config
+
+This configuration will specify the REST API base URL and the WebSocket address.
+
+```
+angular.module('app')
+  .constant('AppConfig', {
+    apiEndpoint: '/xos/api',
+    websocketClient: '/'
+  });
+
+```
+
+## Style Config
+
+This configuration will contain branding information, such as title, logo and navigation items.
+
+```
+angular.module('app')
+  .constant('StyleConfig', {
+    projectName: 'CORD',
+    favicon: 'cord-favicon.png',
+    background: 'cord-bg.jpg',
+    payoff: 'Your VNF orchestrator',
+    logo: 'cord-logo.png',
+    routes: [
+        {
+            label: 'Slices',
+            state: 'xos.core.slices'
+        },
+        {
+            label: 'Instances',
+            state: 'xos.core.instances'
+        },
+        {
+            label: 'Nodes',
+            state: 'xos.core.nodes'
+        }
+    ]
+});
+```
\ No newline at end of file
diff --git a/xos/veestat/conf/app/app.config.production.js b/xos/veestat/conf/app/app.config.production.js
new file mode 100755
index 0000000..de6179f
--- /dev/null
+++ b/xos/veestat/conf/app/app.config.production.js
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+angular.module('app')
+  .constant('AppConfig', {
+    apiEndpoint: '/xos/api',
+    websocketClient: '/'
+  });
diff --git a/xos/veestat/conf/app/style.config.cord.js b/xos/veestat/conf/app/style.config.cord.js
new file mode 100755
index 0000000..022938b
--- /dev/null
+++ b/xos/veestat/conf/app/style.config.cord.js
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+angular.module('app')
+  .constant('StyleConfig', {
+    projectName: 'CORD',
+    favicon: 'cord-favicon.png',
+    background: 'cord-bg.jpg',
+    payoff: 'Your VNF orchestrator',
+    logo: 'cord-logo.png',
+    routes: [
+        {
+            label: 'Slices',
+            state: 'xos.core.slices'
+        },
+        {
+            label: 'Instances',
+            state: 'xos.core.instances'
+        },
+        {
+            label: 'Nodes',
+            state: 'xos.core.nodes'
+        }
+    ]
+});
diff --git a/xos/veestat/conf/app/style.config.opencloud.js b/xos/veestat/conf/app/style.config.opencloud.js
new file mode 100755
index 0000000..2c1776e
--- /dev/null
+++ b/xos/veestat/conf/app/style.config.opencloud.js
@@ -0,0 +1,32 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+angular.module('app')
+  .constant('StyleConfig', {
+    projectName: 'OpenCloud',
+    favicon: 'opencloud-favicon.png',
+    background: 'opencloud-bg.jpg',
+    payoff: 'Your OS resource manager',
+    logo: 'opencloud-logo.png',
+    routes: [
+        {
+            label: 'Slices',
+            state: 'xos.core.slices'
+        }
+    ]
+});
diff --git a/xos/veestat/conf/browsersync-dist.conf.js b/xos/veestat/conf/browsersync-dist.conf.js
new file mode 100755
index 0000000..704b46c
--- /dev/null
+++ b/xos/veestat/conf/browsersync-dist.conf.js
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const conf = require('./gulp.conf');
+
+module.exports = function () {
+  return {
+    server: {
+      baseDir: [
+        conf.paths.dist
+      ]
+    },
+    open: false
+  };
+};
diff --git a/xos/veestat/conf/browsersync.conf.js b/xos/veestat/conf/browsersync.conf.js
new file mode 100755
index 0000000..469468e
--- /dev/null
+++ b/xos/veestat/conf/browsersync.conf.js
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const conf = require('./gulp.conf');
+const proxy = require('./proxy');
+
+module.exports = function () {
+  return {
+    server: {
+      baseDir: [
+        conf.paths.tmp,
+        conf.paths.src
+      ],
+      middleware: function (req, res, next) {
+        if (req.url.indexOf('xosapi') !== -1 || req.url.indexOf('xos') !== -1 || req.url.indexOf('socket') !== -1 || req.url.indexOf('vtn') !== -1) {
+          proxy.api.web(req, res);
+        }
+        else {
+          next();
+        }
+      }
+    },
+    open: false
+  };
+};
\ No newline at end of file
diff --git a/xos/veestat/conf/gulp.conf.js b/xos/veestat/conf/gulp.conf.js
new file mode 100755
index 0000000..ca3edbc
--- /dev/null
+++ b/xos/veestat/conf/gulp.conf.js
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+'use strict';
+
+/**
+ *  This file contains the variables used in other gulp files
+ *  which defines tasks
+ *  By design, we only put there very generic config values
+ *  which are used in several places to keep good readability
+ *  of the tasks
+ */
+
+const path = require('path');
+const gutil = require('gulp-util');
+
+exports.ngModule = 'app';
+
+/**
+ *  The main paths of your project handle these with care
+ */
+exports.paths = {
+  src: 'src',
+  dist: 'dist/extensions/veestat',
+  appConfig: 'conf/app',
+  tmp: '.tmp',
+  e2e: 'e2e',
+  tasks: 'gulp_tasks'
+};
+
+exports.path = {};
+for (const pathName in exports.paths) {
+  if (exports.paths.hasOwnProperty(pathName)) {
+    exports.path[pathName] = function pathJoin() {
+      const pathValue = exports.paths[pathName];
+      const funcArgs = Array.prototype.slice.call(arguments);
+      const joinArgs = [pathValue].concat(funcArgs);
+      return path.join.apply(this, joinArgs);
+    };
+  }
+}
+
+/**
+ *  Common implementation for an error handler of a Gulp plugin
+ */
+exports.errorHandler = function (title) {
+  return function (err) {
+    gutil.log(gutil.colors.red(`[${title}]`), err.toString());
+    this.emit('end');
+  };
+};
diff --git a/xos/veestat/conf/karma-auto.conf.js b/xos/veestat/conf/karma-auto.conf.js
new file mode 100755
index 0000000..f4ce829
--- /dev/null
+++ b/xos/veestat/conf/karma-auto.conf.js
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const conf = require('./gulp.conf');
+const pkg = require('../package.json');
+
+module.exports = function (config) {
+  const configuration = {
+    basePath: '../',
+    singleRun: false,
+    autoWatch: true,
+    logLevel: 'INFO',
+    junitReporter: {
+      outputDir: 'test-reports'
+    },
+    browsers: [
+      'PhantomJS',
+      // 'Chrome'
+    ],
+    frameworks: [
+      'jasmine',
+      'es6-shim'
+    ],
+    files: [
+      'node_modules/es6-shim/es6-shim.js',
+      conf.path.src('index.spec.js'),
+      conf.path.src('**/*.html')
+    ],
+    preprocessors: {
+      [conf.path.src('index.spec.js')]: [
+        'webpack'
+      ],
+      [conf.path.src('**/*.html')]: [
+        'ng-html2js'
+      ]
+    },
+    ngHtml2JsPreprocessor: {
+      stripPrefix: `${conf.paths.src}/`
+    },
+    reporters: ['mocha', 'coverage'],
+    coverageReporter: {
+      type: 'html',
+      dir: 'coverage/'
+    },
+    webpack: require('./webpack-test.conf'),
+    webpackMiddleware: {
+      noInfo: true
+    },
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-junit-reporter'),
+      require('karma-coverage'),
+      require('karma-phantomjs-launcher'),
+      require('karma-chrome-launcher'),
+      require('karma-phantomjs-shim'),
+      require('karma-ng-html2js-preprocessor'),
+      require('karma-webpack'),
+      require('karma-es6-shim'),
+      require('karma-mocha-reporter')
+    ]
+  };
+
+  config.set(configuration);
+};
diff --git a/xos/veestat/conf/karma.conf.js b/xos/veestat/conf/karma.conf.js
new file mode 100755
index 0000000..c8bc5f5
--- /dev/null
+++ b/xos/veestat/conf/karma.conf.js
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const conf = require('./gulp.conf');
+
+module.exports = function (config) {
+  const configuration = {
+    basePath: '../',
+    singleRun: true,
+    autoWatch: false,
+    logLevel: 'INFO',
+    junitReporter: {
+      outputDir: 'test-reports'
+    },
+    browsers: [
+      'PhantomJS'
+    ],
+    frameworks: [
+      'jasmine',
+      'es6-shim'
+    ],
+    files: [
+      'node_modules/es6-shim/es6-shim.js',
+      conf.path.src('index.spec.js'),
+      conf.path.src('**/*.html')
+    ],
+    preprocessors: {
+      [conf.path.src('index.spec.js')]: [
+        'webpack'
+      ],
+      [conf.path.src('**/*.html')]: [
+        'ng-html2js'
+      ]
+    },
+    ngHtml2JsPreprocessor: {
+      stripPrefix: `${conf.paths.src}/`
+    },
+    reporters: ['progress', 'coverage'],
+    coverageReporter: {
+      type: 'html',
+      dir: 'coverage/'
+    },
+    webpack: require('./webpack-test.conf'),
+    webpackMiddleware: {
+      noInfo: true
+    },
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-junit-reporter'),
+      require('karma-coverage'),
+      require('karma-phantomjs-launcher'),
+      require('karma-phantomjs-shim'),
+      require('karma-ng-html2js-preprocessor'),
+      require('karma-webpack'),
+      require('karma-es6-shim')
+    ]
+  };
+
+  config.set(configuration);
+};
diff --git a/xos/veestat/conf/proxy.js b/xos/veestat/conf/proxy.js
new file mode 100644
index 0000000..ab11963
--- /dev/null
+++ b/xos/veestat/conf/proxy.js
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const httpProxy = require('http-proxy');
+
+const target = process.env.PROXY || '192.168.46.100';
+
+const apiProxy = httpProxy.createProxyServer({
+  target: `http://${target}`
+});
+
+apiProxy.on('error', (error, req, res) => {
+  res.writeHead(500, {
+    'Content-Type': 'text/plain'
+  });
+  console.error('[Proxy]', error);
+});
+
+module.exports = {
+  api: apiProxy
+};
diff --git a/xos/veestat/conf/webpack-dist.conf.js b/xos/veestat/conf/webpack-dist.conf.js
new file mode 100755
index 0000000..5f924fb
--- /dev/null
+++ b/xos/veestat/conf/webpack-dist.conf.js
@@ -0,0 +1,124 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const webpack = require('webpack');
+const conf = require('./gulp.conf');
+const path = require('path');
+
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const pkg = require('../package.json');
+const autoprefixer = require('autoprefixer');
+const BaseHrefWebpackPlugin = require('base-href-webpack-plugin').BaseHrefWebpackPlugin;
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const env = process.env.NODE_ENV || 'production';
+const brand = process.env.BRAND || 'cord';
+
+module.exports = {
+  module: {
+    loaders: [
+      {
+        test: /.json$/,
+        loaders: [
+          'json'
+        ]
+      },
+      {
+        test: /\.(css|scss)$/,
+        loaders: ExtractTextPlugin.extract({
+          fallbackLoader: 'style',
+          loader: 'css?minimize!sass!postcss'
+        })
+      },
+      {
+        test: /\.ts$/,
+        exclude: /node_modules/,
+        loaders: [
+          'ng-annotate',
+          'ts'
+        ]
+      },
+      {
+        test: /.html$/,
+        loaders: [
+          'html?' + JSON.stringify({
+            attrs: ["img:src", "img:ng-src"]
+          })
+        ]
+      },
+      {
+        test: /\.(png|woff|woff2|eot|ttf|svg|jpg|gif|jpeg)$/,
+        loader: 'url-loader?limit=100000'
+      }
+    ]
+  },
+  plugins: [
+    new CopyWebpackPlugin([
+      { from: `./conf/app/app.config.${env}.js`, to: `app.config.js` },
+      { from: `./conf/app/style.config.${brand}.js`, to: `style.config.js` },
+    ]),
+    new webpack.optimize.OccurrenceOrderPlugin(),
+    new webpack.NoErrorsPlugin(),
+    new HtmlWebpackPlugin({
+      inject: true,
+      template: conf.path.src('index.html')
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {unused: true, dead_code: true, warnings: false}, // eslint-disable-line camelcase
+      mangle: false // NOTE mangling was breaking the build
+    }),
+    new ExtractTextPlugin('index-[contenthash].css'),
+    new webpack.optimize.CommonsChunkPlugin({name: 'vendor'}),
+    new webpack.ProvidePlugin({
+      $: "jquery",
+      jQuery: "jquery"
+    }),
+    new BaseHrefWebpackPlugin({
+      baseHref: '/spa/'
+    }),
+  ],
+  postcss: () => [autoprefixer],
+  output: {
+    path: path.join(process.cwd(), conf.paths.dist),
+    publicPath: "/xos/", // enable apache proxying on the head node
+    filename: '[name].js'
+  },
+  resolve: {
+    extensions: [
+      '',
+      '.webpack.js',
+      '.web.js',
+      '.js',
+      '.ts'
+    ],
+    alias: {
+      "ngprogress": path.resolve(__dirname, '../node_modules/ngprogress/build/ngProgress.js')
+    }
+  },
+  entry: {
+    app: `./${conf.path.src('index')}`,
+    vendor: Object.keys(pkg.dependencies)
+  },
+  ts: {
+    configFileName: 'tsconfig.json'
+  },
+  tslint: {
+    configuration: require('../tslint.json')
+  }
+};
+
diff --git a/xos/veestat/conf/webpack-test.conf.js b/xos/veestat/conf/webpack-test.conf.js
new file mode 100755
index 0000000..c0115ab
--- /dev/null
+++ b/xos/veestat/conf/webpack-test.conf.js
@@ -0,0 +1,84 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+module.exports = {
+  module: {
+    preLoaders: [
+      {
+        test: /\.ts$/,
+        exclude: /node_modules/,
+        loader: 'tslint'
+      }
+    ],
+    loaders: [
+      {
+        test: /.json$/,
+        loaders: [
+          'json'
+        ]
+      },
+      {
+        test: /\.ts$/,
+        exclude: /node_modules/,
+        loaders: [
+          'ng-annotate',
+          'ts'
+        ]
+      },
+      {
+        test: /.html$/,
+        loaders: [
+          'html?' + JSON.stringify({
+            attrs: ["img:src", "img:ng-src"]
+          })
+        ]
+      },
+      {
+        test: /\.(css|scss)$/,
+        loaders: [
+          'style',
+          'css',
+          'sass',
+          'postcss'
+        ]
+      },
+      {
+        test: /\.(png|woff|woff2|eot|ttf|svg|jpg|gif|jpeg)$/,
+        loader: 'url-loader?limit=100000'
+      }
+    ]
+  },
+  plugins: [],
+  debug: true,
+  devtool: 'source-map',
+  resolve: {
+    extensions: [
+      '',
+      '.webpack.js',
+      '.web.js',
+      '.js',
+      '.ts'
+    ]
+  },
+  ts: {
+    configFileName: 'tsconfig.json'
+  },
+  tslint: {
+    configuration: require('../tslint.json')
+  }
+};
diff --git a/xos/veestat/conf/webpack.conf.js b/xos/veestat/conf/webpack.conf.js
new file mode 100755
index 0000000..4a5d523
--- /dev/null
+++ b/xos/veestat/conf/webpack.conf.js
@@ -0,0 +1,118 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const webpack = require('webpack');
+const conf = require('./gulp.conf');
+const path = require('path');
+
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const autoprefixer = require('autoprefixer');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const env = process.env.NODE_ENV || 'production';
+const brand = process.env.BRAND || 'cord';
+
+module.exports = {
+  module: {
+    preLoaders: [
+      {
+        test: /\.ts$/,
+        exclude: /node_modules/,
+        loader: 'tslint'
+      }
+    ],
+
+    loaders: [
+      {
+        test: /.json$/,
+        loaders: [
+          'json'
+        ]
+      },
+      {
+        test: /\.(css|scss)$/,
+        loaders: [
+          'style',
+          'css',
+          'sass',
+          'postcss'
+        ]
+      },
+      {
+        test: /\.ts$/,
+        exclude: /node_modules/,
+        loaders: [
+          'ng-annotate',
+          'ts'
+        ]
+      },
+      {
+        test: /.html$/,
+        loaders: [
+          'html?' + JSON.stringify({
+            attrs: ["img:src", "img:ng-src"]
+          })
+        ]
+      },
+      {
+        test: /\.(png|woff|woff2|eot|ttf|svg|jpg|gif|jpeg)$/,
+        loader: 'url-loader?limit=100000'
+      }
+    ]
+  },
+  plugins: [
+    new CopyWebpackPlugin([
+      { from: `./conf/app/app.config.${env}.js`, to: `app.config.js` },
+      { from: `./conf/app/style.config.${brand}.js`, to: `style.config.js` },
+    ]),
+    new webpack.IgnorePlugin(/\.\/locale$/),
+    new webpack.optimize.OccurrenceOrderPlugin(),
+    new webpack.NoErrorsPlugin(),
+    new HtmlWebpackPlugin({
+      template: conf.path.src('index.html')
+    })
+  ],
+  postcss: () => [autoprefixer],
+  debug: true,
+  devtool: 'source-map',
+  output: {
+    path: path.join(process.cwd(), conf.paths.tmp),
+    filename: 'index.js'
+  },
+  resolve: {
+    extensions: [
+      '',
+      '.webpack.js',
+      '.web.js',
+      '.js',
+      '.ts'
+    ]
+  },
+  entry: `./${conf.path.src('index')}`,
+  ts: {
+    configFileName: 'tsconfig.json'
+  },
+  tslint: {
+    configuration: require('../tslint.json')
+  },
+  stats: {
+    colors: true,
+    modules: true,
+    reasons: true,
+    errorDetails: true
+  }
+};
diff --git a/xos/veestat/gulp_tasks/browsersync.js b/xos/veestat/gulp_tasks/browsersync.js
new file mode 100755
index 0000000..0e4ada2
--- /dev/null
+++ b/xos/veestat/gulp_tasks/browsersync.js
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const gulp = require('gulp');
+const browserSync = require('browser-sync');
+const spa = require('browser-sync-spa');
+
+const browserSyncConf = require('../conf/browsersync.conf');
+const browserSyncDistConf = require('../conf/browsersync-dist.conf');
+
+browserSync.use(spa());
+
+gulp.task('browsersync', browserSyncServe);
+gulp.task('browsersync:dist', browserSyncDist);
+
+function browserSyncServe(done) {
+  browserSync.init(browserSyncConf());
+  done();
+}
+
+function browserSyncDist(done) {
+  browserSync.init(browserSyncDistConf());
+  done();
+}
diff --git a/xos/veestat/gulp_tasks/karma.js b/xos/veestat/gulp_tasks/karma.js
new file mode 100755
index 0000000..9955d1a
--- /dev/null
+++ b/xos/veestat/gulp_tasks/karma.js
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const path = require('path');
+
+const gulp = require('gulp');
+const karma = require('karma');
+
+gulp.task('karma:single-run', karmaSingleRun);
+gulp.task('karma:auto-run', karmaAutoRun);
+
+function karmaFinishHandler(done) {
+  return failCount => {
+    done(failCount ? new Error(`Failed ${failCount} tests.`) : null);
+  };
+}
+
+function karmaSingleRun(done) {
+  process.env.NODE_ENV = 'test';
+  const configFile = path.join(process.cwd(), 'conf', 'karma.conf.js');
+  const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
+  karmaServer.start();
+}
+
+function karmaAutoRun(done) {
+  process.env.NODE_ENV = 'test';
+  const configFile = path.join(process.cwd(), 'conf', 'karma-auto.conf.js');
+  const karmaServer = new karma.Server({configFile}, karmaFinishHandler(done));
+  karmaServer.start();
+}
diff --git a/xos/veestat/gulp_tasks/misc.js b/xos/veestat/gulp_tasks/misc.js
new file mode 100755
index 0000000..2c9fbb0
--- /dev/null
+++ b/xos/veestat/gulp_tasks/misc.js
@@ -0,0 +1,56 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const path = require('path');
+
+const gulp = require('gulp');
+const del = require('del');
+const filter = require('gulp-filter');
+const rename = require('gulp-rename');
+const replace = require('gulp-replace');
+
+const conf = require('../conf/gulp.conf');
+
+gulp.task('clean', clean);
+gulp.task('other', other);
+
+function clean() {
+  return del([`${conf.paths.dist}/*`, conf.paths.tmp]);
+}
+
+function other() {
+  const fileFilter = filter(file => file.stat.isFile());
+
+  return gulp.src([
+    path.join(conf.paths.src, '/**/*'),
+    path.join(`!${conf.paths.src}`, '/**/*.{scss,ts,html}')
+  ])
+    .pipe(fileFilter)
+    .pipe(gulp.dest(conf.paths.dist));
+}
+
+function other() {
+  const fileFilter = filter(file => file.stat.isFile());
+
+  return gulp.src([
+    path.join(conf.paths.src, '/**/*'),
+    path.join(`!${conf.paths.src}`, '/**/*.{scss,ts,html}')
+  ])
+    .pipe(fileFilter)
+    .pipe(gulp.dest(conf.paths.dist));
+}
diff --git a/xos/veestat/gulp_tasks/webpack.js b/xos/veestat/gulp_tasks/webpack.js
new file mode 100755
index 0000000..ccb64c0
--- /dev/null
+++ b/xos/veestat/gulp_tasks/webpack.js
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const gulp = require('gulp');
+const gutil = require('gulp-util');
+
+const webpack = require('webpack');
+const webpackConf = require('../conf/webpack.conf');
+const webpackDistConf = require('../conf/webpack-dist.conf');
+const gulpConf = require('../conf/gulp.conf');
+const browsersync = require('browser-sync');
+
+gulp.task('webpack:dev', done => {
+  webpackWrapper(false, webpackConf, done);
+});
+
+gulp.task('webpack:watch', done => {
+  webpackWrapper(true, webpackConf, done);
+});
+
+gulp.task('webpack:dist', done => {
+  process.env.NODE_ENV = 'production';
+  webpackWrapper(false, webpackDistConf, done);
+});
+
+function webpackWrapper(watch, conf, done) {
+  const webpackBundler = webpack(conf);
+
+  const webpackChangeHandler = (err, stats) => {
+    if (err) {
+      gulpConf.errorHandler('Webpack')(err);
+    }
+    gutil.log(stats.toString({
+      colors: true,
+      chunks: false,
+      hash: false,
+      version: false
+    }));
+    if (done) {
+      done();
+      done = null;
+    } else {
+      browsersync.reload();
+    }
+  };
+
+  if (watch) {
+    webpackBundler.watch(200, webpackChangeHandler);
+  } else {
+    webpackBundler.run(webpackChangeHandler);
+  }
+}
diff --git a/xos/veestat/gulpfile.js b/xos/veestat/gulpfile.js
new file mode 100755
index 0000000..a15e854
--- /dev/null
+++ b/xos/veestat/gulpfile.js
@@ -0,0 +1,47 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+const gulp = require('gulp');
+const HubRegistry = require('gulp-hub');
+const browserSync = require('browser-sync');
+
+const conf = require('./conf/gulp.conf');
+
+// Load some files into the registry
+const hub = new HubRegistry([conf.path.tasks('*.js')]);
+
+// Tell gulp to use the tasks just loaded
+gulp.registry(hub);
+
+gulp.task('build', gulp.series(gulp.parallel('other', 'webpack:dist')));
+gulp.task('test', gulp.series('karma:single-run'));
+gulp.task('test:auto', gulp.series('karma:auto-run'));
+gulp.task('serve', gulp.series('webpack:watch', 'watch', 'browsersync'));
+gulp.task('serve:dist', gulp.series('default', 'browsersync:dist'));
+gulp.task('default', gulp.series('clean', 'build'));
+gulp.task('watch', watch);
+
+function reloadBrowserSync(cb) {
+  browserSync.reload();
+  cb();
+}
+
+function watch(done) {
+  gulp.watch(conf.path.tmp('index.html'), reloadBrowserSync);
+  done();
+}
diff --git a/xos/veestat/package.json b/xos/veestat/package.json
new file mode 100644
index 0000000..feabe74
--- /dev/null
+++ b/xos/veestat/package.json
@@ -0,0 +1,123 @@
+{
+  "version": "4.0.0",
+  "dependencies": {
+    "angular": "1.6.3",
+    "angular-animate": "1.6.3",
+    "angular-base64": "^2.0.5",
+    "angular-chart.js": "^1.1.1",
+    "angular-cookies": "1.6.3",
+    "angular-resource": "1.6.3",
+    "angular-toastr": "2.1.1",
+    "angular-ui-bootstrap": "2.5.0",
+    "angular-ui-router": "1.0.0-beta.1",
+    "bootstrap": "3.3.7",
+    "bootstrap-sass": "3.3.7",
+    "chart.js": "^2.7.0",
+    "d3": "3.5.17",
+    "jquery": "3.1.1",
+    "lodash": "4.17.4",
+    "moment": "^2.19.0",
+    "ngprogress": "^1.1.1",
+    "oclazyload": "1.1.0",
+    "pluralize": "3.1.0",
+    "rxjs": "5.2.0",
+    "socket.io-client": "1.7.3"
+  },
+  "devDependencies": {
+    "angular-mocks": "1.6.4",
+    "autoprefixer": "6.7.7",
+    "babel-eslint": "6.1.2",
+    "babel-loader": "6.4.1",
+    "babel-plugin-istanbul": "2.0.3",
+    "base-href-webpack-plugin": "1.0.0",
+    "bluebird": "^3.5.0",
+    "browser-sync": "2.18.8",
+    "browser-sync-spa": "1.0.3",
+    "copy-webpack-plugin": "4.0.1",
+    "css-loader": "0.23.1",
+    "del": "2.2.2",
+    "es6-shim": "0.35.3",
+    "eslint": "3.19.0",
+    "eslint-config-angular": "0.5.0",
+    "eslint-config-xo-space": "0.12.0",
+    "eslint-loader": "1.7.1",
+    "eslint-plugin-angular": "1.6.4",
+    "eslint-plugin-babel": "3.3.0",
+    "extract-text-webpack-plugin": "2.0.0-beta.3",
+    "file-loader": "0.9.0",
+    "gitbook-cli": "^2.3.0",
+    "gulp": "gulpjs/gulp#4ed9a4a3275559c73a396eff7e1fde3824951ebb",
+    "gulp-angular-filesort": "1.1.1",
+    "gulp-angular-templatecache": "1.9.1",
+    "gulp-filter": "4.0.0",
+    "gulp-htmlmin": "1.3.0",
+    "gulp-hub": "frankwallis/gulp-hub#d461b9c700df9010d0a8694e4af1fb96d9f38bf4",
+    "gulp-insert": "0.5.0",
+    "gulp-ng-annotate": "1.1.0",
+    "gulp-rename": "1.2.2",
+    "gulp-replace": "0.5.4",
+    "gulp-sass": "2.3.2",
+    "gulp-util": "3.0.8",
+    "html-loader": "0.4.5",
+    "html-webpack-plugin": "2.28.0",
+    "http-proxy": "1.16.2",
+    "istanbul-instrumenter-loader": "2.0.0",
+    "jasmine": "2.5.3",
+    "jasmine-jquery": "2.1.1",
+    "jasmine-spec-reporter": "^4.0.0",
+    "json-loader": "0.5.4",
+    "karma": "1.6.0",
+    "karma-angular-filesort": "1.0.2",
+    "karma-chrome-launcher": "2.0.0",
+    "karma-coverage": "1.1.1",
+    "karma-es6-shim": "1.0.0",
+    "karma-jasmine": "1.1.0",
+    "karma-junit-reporter": "1.2.0",
+    "karma-mocha-reporter": "2.2.3",
+    "karma-ng-html2js-preprocessor": "0.2.2",
+    "karma-phantomjs-launcher": "1.0.4",
+    "karma-phantomjs-shim": "1.4.0",
+    "karma-webpack": "1.8.1",
+    "ng-annotate-loader": "0.0.10",
+    "node-sass": "3.13.1",
+    "phantomjs-prebuilt": "2.1.14",
+    "postcss-loader": "0.8.2",
+    "remap-istanbul": "0.9.5",
+    "resolve-url-loader": "1.6.1",
+    "sass-loader": "3.2.3",
+    "style-loader": "0.13.2",
+    "ts-loader": "0.8.2",
+    "tslint": "3.15.1",
+    "tslint-loader": "2.1.5",
+    "typescript": "2.2.2",
+    "typings": "1.5.0",
+    "url-loader": "0.5.8",
+    "webpack": "2.1.0-beta.20"
+  },
+  "scripts": {
+    "postinstall": "npm run typings",
+    "build": "gulp",
+    "start": "gulp serve",
+    "typings": "typings install",
+    "serve:dist": "gulp serve:dist",
+    "serve:dist:watch": "gulp serve:dist:watch",
+    "test": "gulp test",
+    "test:auto": "gulp test:auto",
+    "test:e2e": "protractor conf/protractor.conf.js",
+    "config": "gulp config",
+    "lint": "tslint -c ./tslint.json 'src/**/*.ts'"
+  },
+  "eslintConfig": {
+    "globals": {
+      "expect": true
+    },
+    "root": true,
+    "env": {
+      "browser": true,
+      "jasmine": true
+    },
+    "extends": [
+      "xo-space/esnext"
+    ]
+  }
+}
diff --git a/xos/veestat/src/app/components/cfmlist.component.html b/xos/veestat/src/app/components/cfmlist.component.html
new file mode 100644
index 0000000..a17afa4
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.html
@@ -0,0 +1,82 @@
+<!--
+Copyright 2017-present Open Networking Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<div class="row">
+  <div class="col-xs-12">
+    <h1>CFM/OAM Jitter and Delay Monitoring</h1>
+    <p>Select the appropriate MD, MA, MEP, and DM for which you would like to monitor delay and jitter statistics.</p>
+    <a class="btn btn-success"
+       ng-style="{'visibility': vm.displayButton ? 'visible' : 'hidden'}"
+       ng-click="vm.stateChange()"
+    >
+      View Delay and Jitter
+    </a>
+  </div>
+</div>
+
+<div class="row">
+  <div class="col-xs-3">
+    <h4>Maintenance Domains (MD)</h4>
+  </div>
+  <div class="col-xs-3">
+    <h4>Maintenance Associations (MA)</h4>
+  </div>
+  <div class="col-xs-3">
+    <h4>MA Endpoints (MEP)</h4>
+  </div>
+  <div class="col-xs-3">
+    <h4>Delay Measurements (DM)</h4>
+  </div>
+</div>
+
+<div class="row">
+  <div class="col-xs-3">
+    <div class="form-group">
+      <select class="form-control cfmlist" id="md" size="25" ng-model="vm.mdValue" ng-change="vm.triggerRefresh('md')">
+        <option ng-repeat="md in vm.mds" value="{{md.mdNumericId}}">{{md.mdName}} (ID {{md.mdNumericId}})</option>
+      </select>
+    </div>
+  </div>
+  <div class="col-xs-3">
+    <div class="form-group">
+      <select class="form-control cfmlist" id="ma" size="25" ng-model="vm.maValue" ng-change="vm.triggerRefresh('ma')">
+        <option ng-repeat="ma in vm.mas" value="{{ma.maNumericId}}">{{ma.maName}} (ID {{ma.maNumericId}})</option>
+      </select>
+    </div>
+  </div>
+  <div class="col-xs-3">
+    <div class="form-group">
+      <select class="form-control cfmlist" id="mep" size="25" ng-model="vm.mepValue" ng-change="vm.triggerRefresh('mep')">
+        <option ng-repeat="mep in vm.meps" value="{{mep.mepId}}">{{mep.mepId}}</option>
+      </select>
+    </div>
+  </div>
+  <div class="col-xs-3">
+    <div class="form-group">
+      <select class="form-control cfmlist" id="dm" size="25" ng-model="vm.dmValue" ng-change="vm.triggerRefresh('dm')">
+        <!-- <option ng-repeat="dm in vm.dms" value="{{dm.dmId}}">{{dm.dmId}}</option> -->
+        <option value="1" ng-show="vm.displaydms">1</option>
+        <option value="2" ng-show="vm.displaydms">2</option>
+      </select>
+    </div>
+  </div>
+</div>
+
+<div class="row">
+  <div class="col-xs-12">
+    <p>It may take some time for MAs and MEPs to be loaded. Thank you for your patience.</p>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmlist.component.scss b/xos/veestat/src/app/components/cfmlist.component.scss
new file mode 100644
index 0000000..0a42875
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.scss
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.cfmlist > option {
+  color: #fff;
+}
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmlist.component.ts b/xos/veestat/src/app/components/cfmlist.component.ts
new file mode 100644
index 0000000..db1920d
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.ts
@@ -0,0 +1,130 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import {
+  IMicrosemiMaFormat, IMicrosemiMdFormat, IMicrosemiMepFormat, IMicrosemiStats,
+  IMicrosemiStatsData
+} from './stats-service';
+import './cfmlist.component.scss';
+import * as _ from 'lodash';
+
+class CfmlistComponent {
+
+  static $inject = [
+    'MicrosemiStats',
+    '$state'
+  ];
+
+  public mds: IMicrosemiMdFormat[];
+  public mas: IMicrosemiMaFormat[];
+  public meps: IMicrosemiMepFormat[];
+  public dms: IMicrosemiStatsData[];
+
+  public md: any;
+  public ma: any;
+  public mdValue: number;
+  public maValue: number;
+  public mepValue: number;
+  public dmValue: number;
+
+  public displayButton = false;
+  public displaydms = false;
+
+  constructor (
+    private MicrosemiStats : IMicrosemiStats,
+    private $state: any
+  ) {}
+
+  $onInit() {
+    this.MicrosemiStats.getMds().then((res) => {
+      this.mds = res;
+    });
+  }
+
+  public triggerRefresh(changed: string) {
+    switch (changed) {
+      case 'md':
+        this.meps = [];
+        this.maValue = null;
+        this.mepValue = null;
+        this.getMas(this.mdValue);
+        this.displayButton = false;
+        this.displaydms = false;
+        break;
+      case 'ma':
+        this.mepValue = null;
+        this.dmValue = null;
+        this.getMeps(this.maValue);
+        this.displayButton = false;
+        this.displaydms = false;
+        break;
+      case 'mep':
+        this.dmValue = null;
+        this.getDms(this.mepValue);
+        this.displayButton = false;
+        this.displaydms = true;
+        break;
+      case 'dm':
+        this.displayButton = true;
+        break;
+      default:
+        break;
+    }
+
+  }
+
+  public getMas(mdNumericId: number) {
+    this.md = _.find(this.mds, (md) => {
+      return md.mdNumericId === Number(mdNumericId);
+    });
+    this.mas = this.md.maList;
+  }
+
+  public getMeps(maNumericId: number) {
+    this.ma = _.find(this.mas, (ma) => {
+      return ma.maNumericId === Number(maNumericId);
+    });
+    this.MicrosemiStats.getMeps(this.md.mdName, this.ma.maName)
+      .then((res: IMicrosemiMepFormat[]) => {
+        this.meps = res;
+      });
+  }
+
+  public getDms(mepNumericId: number) {
+    this.MicrosemiStats.getDms(this.md.mdName, this.ma.maName, String(mepNumericId))
+      .then((res: IMicrosemiStatsData[]) => {
+        this.dms = res;
+      });
+  }
+
+  public stateChange() {
+    this.$state.go('xos.cfmstat', {
+      mdName: this.md.mdName,
+      maName: this.ma.maName,
+      mepId: this.mepValue,
+      dmId: this.dmValue
+    });
+  }
+
+}
+
+export const cfmlistComponent: angular.IComponentOptions = {
+  template: require('./cfmlist.component.html'),
+  controllerAs: 'vm',
+  controller: CfmlistComponent
+};
diff --git a/xos/veestat/src/app/components/cfmstat.component.html b/xos/veestat/src/app/components/cfmstat.component.html
new file mode 100644
index 0000000..8831645
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.html
@@ -0,0 +1,82 @@
+
+<!--
+Copyright 2017-present Open Networking Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<div class="row">
+    <div class="col-md-12">
+        <h1>Delay and Jitter Stats</h1>
+    </div>
+    <div ng-hide="vm.lineChart.data.length > 0">
+        <div class="loader">
+            Loading
+        </div>
+    </div>
+    <div class="row" ng-show="vm.lineChart.data.length > 0">
+        <div class="col-sm-8">
+            <h3>{{vm.graphTitle}}</h3>
+            <canvas class="chart chart-line" chart-data="vm.lineChart.data" chart-dataset-override="vm.lineChart.dataset"
+                    chart-labels="vm.lineChart.labels" chart-series="vm.lineChart.series" chart-options="vm.lineOptions" chart-colors="vm.lineColors">
+            </canvas>
+            <div class="row">
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+                    <div class="square" style="background-color: #ffa500"></div>
+                    <label>Jitter Max</label>
+                </div>
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+                    <div class="square" style="background-color: #c55d01"></div>
+                    <label>Jitter Average</label>
+                </div>
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+                    <div class="square" style="background-color: #8b0000"></div>
+                    <label>Jitter Min</label>
+                </div>
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+                    <div class="square" style="background-color: #add8e6"></div>
+                    <label>Delay Max</label>
+                </div>
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+                    <div class="square" style="background-color: #6f6db9"></div>
+                    <label>Delay Average</label>
+                </div>
+                <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+                    <div class="square" style="background-color: #00008b"></div>
+                    <label>Delay Min</label>
+                </div>
+            </div>
+            <div class="btn-group">
+                <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'both'" ng-click="vm.renderGraph()">Delay and Jitter</label>
+                <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'delay'" ng-click="vm.renderGraph()">Delay only</label>
+                <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'jitter'" ng-click="vm.renderGraph()">Jitter only</label>
+            </div>
+        </div>
+        <div class="col-sm-4">
+            <div class="row">
+                <div class="col-sm-12" style="margin-bottom: 50px">
+                    <h3>Current Delay Bin</h3>
+                    <canvas class="chart chart-bar" chart-data="vm.delayBin.data" chart-dataset-override="vm.delayBin.dataset"
+                            chart-labels="vm.delayBin.labels" chart-series="vm.delayBin.series" chart-options="vm.options">
+                    </canvas>
+                </div>
+                <div class="col-sm-12">
+                    <h3>Current Jitter Bin</h3>
+                    <canvas class="chart chart-bar" chart-data="vm.jitterBin.data" chart-dataset-override="vm.jitterBin.dataset"
+                            chart-labels="vm.jitterBin.labels" chart-series="vm.jitterBin.series" chart-options="vm.options">
+                    </canvas>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmstat.component.scss b/xos/veestat/src/app/components/cfmstat.component.scss
new file mode 100644
index 0000000..697b74d
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.scss
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.square{
+  width: 10px;
+  height: 10px;
+  display: inline-block;
+  margin-right: 5px;
+}
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmstat.component.ts b/xos/veestat/src/app/components/cfmstat.component.ts
new file mode 100644
index 0000000..e2b78de
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.ts
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {IMicrosemiStats, IMicrosemiStatsData} from './stats-service';
+import './cfmstat.component.scss';
+
+class CfmStatComponent {
+  static $inject = [
+    '$interval',
+    '$stateParams',
+    'MicrosemiStats'
+  ];
+
+  public options = {
+    animation: {
+      duration: 0
+    },
+    scales: {
+      yAxes: [{
+        ticks: {
+          beginAtZero: true,
+          fontColor: '#fff',
+        },
+        gridLines: {
+          color: '#555',
+          zeroLineColor: '#888'
+        }
+      }],
+      xAxes: [{
+        ticks: {
+          beginAtZero: true,
+          fontColor: '#fff',
+        },
+        gridLines: {
+          color: '#555'
+        }
+      }]
+    },
+
+  };
+
+  public lineOptions = {
+    animation: {
+      duration: 0
+    },
+    scales: {
+      yAxes: [{
+        ticks: {
+          beginAtZero: true,
+          callback: val => `${(Math.round(val * 1000) / 1000).toFixed(3)} ms`,
+          fontColor: '#fff',
+        },
+        gridLines: {
+          color: '#555',
+          zeroLineColor: '#888'
+        }
+      }],
+      xAxes: [{
+        ticks: {
+          beginAtZero: true,
+          fontColor: '#fff',
+        },
+        gridLines: {
+          color: '#555',
+        }
+      }]
+    },
+    bezierCurve: false
+  };
+
+  public lineChart = {
+    series: ['Jitter Avg', 'Jitter Max', 'Jitter Min', 'Delay Avg', 'Delay Max', 'Delay Min'],
+    labels: [],
+    data: [],
+    dataset: []
+  };
+
+  public delayBin = {
+    series: ['Current Delay Bin'],
+    labels: ['0 - 10 ms', '10 - 20 ms', '20 - 37 ms', '> 37 ms'],
+    data: [],
+    dataset: {
+      backgroundColor: '#6f6db9'
+    }
+  };
+
+  public jitterBin = {
+    series: ['Current Jitter Bin'],
+    labels: ['0 - 3 ms', '3 - 8 ms', '8 - 100 ms', '> 100 ms'],
+    data: [],
+    dataset: {
+      backgroundColor: '#ffa500'
+    }
+  };
+
+  public allLineColors = ['#c55d01', '#ffa500', '#8b0000', '#6f6db9', '#add8e6', '#00008b'];
+  public lineColors = this.allLineColors;
+  public lineFill = [
+    {fill: false, borderColor: '#c55d01'},
+    {fill: false, borderColor: '#ffa500'},
+    {fill: false, borderColor: '#8b0000'},
+    {fill: false, borderColor: '#6f6db9'},
+    {fill: false, borderColor: '#add8e6'},
+    {fill: false, borderColor: '#00008b'}];
+
+  public graphOption = 'both';
+  public graphTitle = 'Delay and Jitter Graph';
+  public linedata = [];
+
+  constructor(
+    private $interval: ng.IIntervalService,
+    private $stateParams: ng.ui.IStateParamsService,
+    private MicrosemiStats: IMicrosemiStats,
+  ) {
+
+  }
+
+  $onInit() {
+    const load = () => {
+      this.MicrosemiStats.get(this.$stateParams.mdName, this.$stateParams.maName, this.$stateParams.mepId, this.$stateParams.dmId || 1)
+        .then((res: IMicrosemiStatsData) => {
+          const data = this.MicrosemiStats.getAllData(res);
+
+          this.linedata = data.line.data;
+          this.lineChart.labels = data.line.labels;
+          this.lineChart.data = data.line.data;
+          this.delayBin.data = data.delay.data;
+          this.jitterBin.data = data.jitter.data;
+
+        });
+    };
+
+    load();
+
+    this.renderGraph();
+
+    this.$interval(() => {
+      load();
+    }, 1000 * 60);
+  }
+
+  renderGraph() : void {
+    this.graphTitle = this.graphTitleRender();
+    this.graphDisplayRender();
+  }
+
+  graphTitleRender() : string {
+    let output;
+    switch (this.graphOption) {
+      default:
+      case 'both':
+        output = 'Delay and Jitter Graph';
+        break;
+      case 'delay':
+        output = 'Delay Graph';
+        break;
+      case 'jitter':
+        output = 'Jitter Graph';
+        break;
+    }
+    return output;
+  }
+
+  graphDisplayRender() : void {
+    switch (this.graphOption) {
+
+      default:
+      case 'both':
+        this.lineChart.series = ['Jitter Avg', 'Jitter Max', 'Jitter Min', 'Delay Avg', 'Delay Max', 'Delay Min'];
+        this.lineChart.data = this.linedata;
+        this.lineChart.dataset = this.lineFill;
+        this.lineColors = this.allLineColors;
+        break;
+
+      case 'delay':
+        this.lineChart.series = ['Delay Avg', 'Delay Max', 'Delay Min'];
+        this.lineChart.data = this.linedata.slice(3);
+        this.lineChart.dataset = this.lineFill.slice(3);
+        this.lineColors = this.allLineColors.slice(3);
+        break;
+
+      case 'jitter':
+        this.lineChart.series = ['Jitter Avg', 'Jitter Max', 'Jitter Min'];
+        this.lineChart.data = this.linedata.slice(0, 3);
+        this.lineChart.dataset = this.lineFill.slice(0, 3);
+        this.lineColors = this.allLineColors.slice(0, 3);
+        break;
+    }
+  }
+
+}
+
+
+export const cfmStatComponent: angular.IComponentOptions = {
+  template: require('./cfmstat.component.html'),
+  controllerAs: 'vm',
+  controller: CfmStatComponent
+};
diff --git a/xos/veestat/src/app/components/stats-service.ts b/xos/veestat/src/app/components/stats-service.ts
new file mode 100644
index 0000000..ef506ca
--- /dev/null
+++ b/xos/veestat/src/app/components/stats-service.ts
@@ -0,0 +1,286 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as _ from 'lodash';
+import IHttpPromiseCallbackArg = angular.IHttpPromiseCallbackArg;
+
+export interface IMicrosemiStatsBinData {
+  lowerLimit: string;
+  count: number;
+}
+
+export interface IMicrosemiStatsBin {
+  bins: IMicrosemiStatsBinData[];
+}
+
+export interface IMicrosemiStatsFormat {
+  interFrameDelayVariationTwoWayAvg: string;
+  interFrameDelayVariationTwoWayMax: string;
+  interFrameDelayVariationTwoWayMin: string;
+  interFrameDelayVariationTwoWayBins: IMicrosemiStatsBin;
+  FrameDelayTwoWayAvg: string;
+  FrameDelayTwoWayMax: string;
+  FrameDelayTwoWayMin: string;
+  FrameDelayTwoWayBins: IMicrosemiStatsBin;
+  [x: string]: any;
+}
+
+export interface IMicrosemiStatsData {
+  dm: {
+    dmId: any;
+    current: IMicrosemiStatsFormat;
+    historic: IMicrosemiStatsFormat[];
+    [x: string]: any;
+  };
+  dmId?: any;
+  current?: IMicrosemiStatsFormat;
+  historic?: IMicrosemiStatsFormat[];
+  [x: string]: any;
+
+}
+
+export interface IMicrosemiChartData {
+  labels: string[];
+  data: [number[]];
+}
+
+export interface IMicrosemiStats {
+  getMds(): any;
+  getMeps(mdName: string, maName: string): any;
+  getDms(mdName: string, maName: string, mepId: string): any;
+  get(mdName: string, maName: string, mepId: string, dmId: string): ng.IPromise<any>;
+  getAllData(res: IMicrosemiStatsData): any;
+}
+
+export interface IMicrosemiMdData {
+  mds?: [IMicrosemiMdFormat[]];
+  md?: IMicrosemiMdFormat;
+}
+
+export interface IMicrosemiMdFormat {
+  mdName: string;
+  mdNameType: string;
+  mdLevel: string;
+  mdNumericId: number;
+  maList: any[];
+}
+
+export interface IMicrosemiMaData {
+  ma: IMicrosemiMaFormat[];
+}
+
+export interface IMicrosemiMaFormat {
+  maName: string;
+  maNameType: string;
+  maNumericId: number;
+  'ccm-interval': string;
+  componentList: any[];
+  'rmep-list': any[];
+}
+
+export interface IMicrosemiMepData {
+  meps: [IMicrosemiMepFormat[]];
+}
+
+export interface IMicrosemiMepFormat {
+  mepId: number;
+  deviceId: string;
+  port: string;
+  direction: string;
+  mdName: string;
+  maName: string;
+  'administrative-state': boolean;
+  'cci-enabled': boolean;
+  'ccm-ltm-priority': number;
+  macAddress: string;
+  loopback: any;
+  remoteMeps: any[];
+  activeMacStatusDefect: boolean;
+  activeRdiCcmDefect: boolean;
+
+}
+
+export interface IMicrosemiDmList {
+  dms: [IMicrosemiStatsData[]];
+}
+
+export class MicrosemiStats implements IMicrosemiStats {
+
+  static $inject = [
+    '$q',
+    '$http',
+    '$log',
+    '$base64'
+  ];
+
+  private auth = this.$base64.encode('onos:rocks');
+  private headers = {
+    'Authorization': `Basic ${this.auth}`
+  };
+
+  constructor (
+    private $q: ng.IQService,
+    private $http: ng.IHttpService,
+    private $log: ng.ILogService,
+    private $base64: any
+  ) {
+
+  }
+
+  public getMds() {
+    const d = this.$q.defer();
+    this.$http.get(`/vtn/onos/cfm/md/`, {'headers': this.headers})
+      .then((res: IHttpPromiseCallbackArg<IMicrosemiMdData>) => {
+        d.resolve(res.data.mds[0]);
+      })
+      .catch(err => {
+        this.$log.error(`[CFMStatService] Error in GET md: `, err);
+      });
+    return d.promise;
+  }
+
+  public getMeps(mdName: string, maName: string) {
+    const d = this.$q.defer();
+    this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/`, {'headers': this.headers})
+      .then((res: IHttpPromiseCallbackArg<IMicrosemiMepData>) => {
+        d.resolve(res.data.meps[0]);
+      })
+      .catch(err => {
+        this.$log.error(`[CFMStatService] Error in GET mep: `, err);
+      });
+    return d.promise;
+  }
+
+  public getDms(mdName : string, maName: string, mepId: string) {
+    const d = this.$q.defer();
+    this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/${mepId}/dm/`, {'headers': this.headers})
+      .then((res: IHttpPromiseCallbackArg<IMicrosemiDmList>) => {
+        d.resolve(res.data.dms[0]);
+      })
+      .catch(err => {
+        this.$log.error(`[CFMStatService] Error in GET dm list: `, err);
+      });
+    return d.promise;
+  }
+
+  public get(mdName : string, maName: string, mepId: string, dmId: string) {
+    const d = this.$q.defer();
+
+    this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/${mepId}/dm/${dmId}`, {'headers': this.headers})
+      .then((res) => {
+        d.resolve(res);
+      })
+      .catch(err => {
+        this.$log.error(`[CFMStatService] Error in GET: `, err);
+      });
+    return d.promise;
+  }
+
+  public getAllData(res: IMicrosemiStatsData) {
+    const data = res['data'];
+    const line = this.parseDataForLine(data);
+    const delay = this.parseDelayBin(data);
+    const jitter = this.parseJitterBin(data);
+    return {
+      line,
+      delay,
+      jitter
+    };
+  }
+
+  private getNumericVal(val: string): number {
+    if (!val) {
+      return 0;
+    }
+    val = val.replace('PT', '');
+    val = val.replace('S', '');
+    return parseFloat(val) * 1000;
+  }
+
+  private formatDate(string: string): string {
+    const date = new Date(string);
+    return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
+  }
+
+  private parseDelayBin(stats: IMicrosemiStatsData) {
+    const labels: number[] = [];
+    const data: number[] = [];
+    _.forEach(stats.dm.current.frameDelayTwoWayBins.bins, (bin) => {
+      labels.push(this.getNumericVal(bin.lowerLimit));
+      data.push(bin.count);
+    });
+    return {
+      labels,
+      data
+    };
+  }
+
+  private parseJitterBin(stats: IMicrosemiStatsData) {
+    const labels: number[] = [];
+    const data: number[] = [];
+    _.forEach(stats.dm.current.interFrameDelayVariationTwoWayBins.bins, (bin) => {
+      labels.push(this.getNumericVal(bin.lowerLimit));
+      data.push(bin.count);
+    });
+    return {
+      labels,
+      data
+    };
+  }
+
+  private parseDataForLine(data: IMicrosemiStatsData): IMicrosemiChartData {
+    let stats = angular.copy(data.dm.historic);
+    stats.unshift(data.dm.current);
+
+    const jitterAvg: number[] = [];
+    const jitterMax: number[] = [];
+    const jitterMin: number[] = [];
+    const delayAvg: number[] = [];
+    const delayMax: number[] = [];
+    const delayMin: number[] = [];
+    const labels: string[] = [];
+
+    _.forEach(stats, (s: IMicrosemiStatsFormat) => {
+      const j_avg = this.getNumericVal(s.interFrameDelayVariationTwoWayAvg);
+      const j_max = this.getNumericVal(s.interFrameDelayVariationTwoWayMax);
+      const j_min = this.getNumericVal(s.interFrameDelayVariationTwoWayMin);
+      const d_avg = this.getNumericVal(s.frameDelayTwoWayAvg);
+      const d_max = this.getNumericVal(s.frameDelayTwoWayMax);
+      const d_min = this.getNumericVal(s.frameDelayTwoWayMin);
+      jitterAvg.push(j_avg);
+      jitterMax.push(j_max);
+      jitterMin.push(j_min);
+      delayAvg.push(d_avg);
+      delayMax.push(d_max);
+      delayMin.push(d_min);
+      labels.push(s.endTime ? this.formatDate(s.endTime) : 'current');
+    });
+
+    return {
+      labels: labels.reverse(),
+      data: [
+        jitterAvg.reverse(),
+        jitterMax.reverse(),
+        jitterMin.reverse(),
+        delayAvg.reverse(),
+        delayMax.reverse(),
+        delayMin.reverse(),
+      ]
+    };
+  }
+
+}
diff --git a/xos/veestat/src/index.html b/xos/veestat/src/index.html
new file mode 100644
index 0000000..6c19ebc
--- /dev/null
+++ b/xos/veestat/src/index.html
@@ -0,0 +1,35 @@
+
+<!--
+Copyright 2017-present Open Networking Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+
+<!DOCTYPE html>
+<html lang="en" ng-app="veestat">
+<head>
+  <meta charset="UTF-8">
+  <title>veestat</title>
+  <link href="/xos/loader.css" rel="stylesheet">
+  <link href="/xos/app.css" rel="stylesheet">
+</head>
+<body>
+  <div ui-view></div>
+  <script src="/xos/vendor.js"></script>
+  <script src="/xos/app.js"></script>
+  <script src="/xos/loader.js"></script>
+  <script src="/xos/app.config.js"></script>
+  <script src="/xos/style.config.js"></script>
+</body>
+</html>
diff --git a/xos/veestat/src/index.ts b/xos/veestat/src/index.ts
new file mode 100644
index 0000000..c8bbad3
--- /dev/null
+++ b/xos/veestat/src/index.ts
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/// <reference path="../typings/index.d.ts" />
+import * as angular from 'angular';
+
+import 'angular-ui-router';
+import 'angular-resource';
+import 'angular-cookies';
+import 'chart.js';
+import 'angular-chart.js';
+import 'angular-base64';
+
+import routesConfig from './routes';
+import {cfmStatComponent} from './app/components/cfmstat.component';
+import {MicrosemiStats} from './app/components/stats-service';
+import {cfmlistComponent} from './app/components/cfmlist.component';
+
+angular.module('veestat', [
+    'ui.router',
+    'app',
+    'chart.js',
+    'base64'
+  ])
+  .config(routesConfig)
+  .component('cfmstats', cfmStatComponent)
+  .component('cfmlist', cfmlistComponent)
+  .service('MicrosemiStats', MicrosemiStats)
+  .run(function(
+    $log: ng.ILogService,
+    $state: ng.ui.IStateService,
+    XosNavigationService: any) {
+    $log.info('[CFM Stats] App is running');
+
+    XosNavigationService.add({
+      label: 'CFM Stats',
+      state: 'xos.cfmlist',
+    });
+
+  });
diff --git a/xos/veestat/src/routes.ts b/xos/veestat/src/routes.ts
new file mode 100644
index 0000000..27093b2
--- /dev/null
+++ b/xos/veestat/src/routes.ts
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+export default routesConfig;
+
+function routesConfig($stateProvider: angular.ui.IStateProvider, $locationProvider: angular.ILocationProvider) {
+  $locationProvider.html5Mode(false).hashPrefix('');
+
+  $stateProvider
+    .state('xos.cfmlist', {
+      url: 'cfmlist',
+      parent: 'xos',
+      component: 'cfmlist'
+    })
+    .state('xos.cfmstat', {
+      url: 'cfmstat',
+      parent: 'xos',
+      component: 'cfmstats',
+      params: {mdName: null, maName: null, mepId: null, dmId: null}
+    });
+}
diff --git a/xos/veestat/tsconfig.json b/xos/veestat/tsconfig.json
new file mode 100644
index 0000000..c516fb7
--- /dev/null
+++ b/xos/veestat/tsconfig.json
@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "sourceMap": true,
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "removeComments": false,
+    "noImplicitAny": false
+  },
+  "compileOnSave": false,
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx"
+  ],
+  "exclude": [
+    "typings/**",
+    "conf/app/**",
+    "node_modules"
+  ]
+}
diff --git a/xos/veestat/tslint.json b/xos/veestat/tslint.json
new file mode 100644
index 0000000..04d33e1
--- /dev/null
+++ b/xos/veestat/tslint.json
@@ -0,0 +1,84 @@
+{
+  "rules": {
+    "ban": [true,
+      ["_", "extend"],
+      ["_", "isNull"],
+      ["_", "isDefined"]
+    ],
+    "class-name": true,
+    "comment-format": [true,
+      "check-space"
+    ],
+    "curly": true,
+    "eofline": true,
+    "forin": true,
+    "indent": [true, "spaces"],
+    "interface-name": true,
+    "jsdoc-format": true,
+    "label-position": true,
+    "label-undefined": true,
+    "max-line-length": [false, 140],
+    "member-ordering": [true,
+      "public-before-private",
+      "static-before-instance",
+      "variables-before-functions"
+    ],
+    "no-arg": true,
+    "no-bitwise": true,
+    "no-console": [true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace",
+      "log",
+      "error"
+    ],
+    "no-construct": true,
+    "no-constructor-vars": false,
+    "no-debugger": true,
+    "no-duplicate-key": true,
+    "no-duplicate-variable": true,
+    "no-empty": true,
+    "no-eval": true,
+    "no-string-literal": false,
+    "no-switch-case-fall-through": true,
+    "trailing-comma": true,
+    "no-trailing-whitespace": true,
+    "no-unused-expression": true,
+    "no-unused-variable": true,
+    "no-unreachable": true,
+    "no-use-before-declare": true,
+    "no-var-requires": true,
+    "one-line": [true,
+      "check-open-brace",
+      "check-catch",
+      "check-whitespace"
+    ],
+    "quotemark": [true, "single"],
+    "radix": true,
+    "semicolon": true,
+    "triple-equals": [true, "allow-null-check"],
+    "typedef": [true,
+      "callSignature",
+      "indexSignature",
+      "parameter",
+      "propertySignature",
+      "variableDeclarator"
+    ],
+    "typedef-whitespace": [true,
+      ["callSignature", "noSpace"],
+      ["catchClause", "noSpace"],
+      ["indexSignature", "space"]
+    ],
+    "use-strict": false,
+    "variable-name": false,
+    "whitespace": [true,
+      "check-branch",
+      "check-decl",
+      "check-operator",
+      "check-separator",
+      "check-type"
+    ]
+  }
+}
diff --git a/xos/veestat/typings.json b/xos/veestat/typings.json
new file mode 100644
index 0000000..a830275
--- /dev/null
+++ b/xos/veestat/typings.json
@@ -0,0 +1,18 @@
+{
+  "globalDependencies": {
+    "angular": "registry:dt/angular#1.5.0+20161208205636",
+    "angular-cookies": "registry:dt/angular-cookies#1.4.0+20160317120654",
+    "angular-mocks": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular-mocks.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a",
+    "angular-resource": "registry:dt/angular-resource#1.5.0+20161114123626",
+    "angular-ui-router": "registry:dt/angular-ui-router#1.1.5+20160707113237",
+    "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504",
+    "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a",
+    "jasmine-jquery": "registry:dt/jasmine-jquery#1.5.8+20161128184045",
+    "jquery": "registry:dt/jquery#1.10.0+20161119044246",
+    "require": "registry:dt/require#2.1.20+20160316155526",
+    "socket.io-client": "registry:dt/socket.io-client#1.4.4+20160317120654"
+  },
+  "dependencies": {
+    "angular-toastr": "registry:dt/angular-toastr#1.6.0+20160708003927"
+  }
+}
diff --git a/xos/veestat/veestat.yml b/xos/veestat/veestat.yml
new file mode 100644
index 0000000..7ab0b05
--- /dev/null
+++ b/xos/veestat/veestat.yml
@@ -0,0 +1,31 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: veestat
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # UI Extension
+    veestat:
+      type: tosca.nodes.XOSGuiExtension
+      properties:
+        files: /xos/extensions/veestat/vendor.js, /xos/extensions/veestat/app.js