Merged master
diff --git a/containers/nagios/Dockerfile b/containers/nagios/Dockerfile
new file mode 100644
index 0000000..c33f71c
--- /dev/null
+++ b/containers/nagios/Dockerfile
@@ -0,0 +1,63 @@
+FROM cpuguy83/ubuntu
+ENV NAGIOS_HOME /opt/nagios
+ENV NAGIOS_USER nagios
+ENV NAGIOS_GROUP nagios
+ENV NAGIOS_CMDUSER nagios
+ENV NAGIOS_CMDGROUP nagios
+ENV NAGIOSADMIN_USER nagiosadmin
+ENV NAGIOSADMIN_PASS nagios
+ENV APACHE_RUN_USER nagios
+ENV APACHE_RUN_GROUP nagios
+ENV NAGIOS_TIMEZONE UTC
+
+RUN sed -i 's/universe/universe multiverse/' /etc/apt/sources.list
+RUN apt-get update && apt-get install -y iputils-ping netcat build-essential snmp snmpd snmp-mibs-downloader php5-cli apache2 libapache2-mod-php5 runit bc postfix bsd-mailx
+RUN ( egrep -i  "^${NAGIOS_GROUP}" /etc/group || groupadd $NAGIOS_GROUP ) && ( egrep -i "^${NAGIOS_CMDGROUP}" /etc/group || groupadd $NAGIOS_CMDGROUP )
+RUN ( id -u $NAGIOS_USER || useradd --system $NAGIOS_USER -g $NAGIOS_GROUP -d $NAGIOS_HOME ) && ( id -u $NAGIOS_CMDUSER || useradd --system -d $NAGIOS_HOME -g $NAGIOS_CMDGROUP $NAGIOS_CMDUSER )
+
+ADD http://downloads.sourceforge.net/project/nagios/nagios-3.x/nagios-3.5.1/nagios-3.5.1.tar.gz?r=http%3A%2F%2Fwww.nagios.org%2Fdownload%2Fcore%2Fthanks%2F%3Ft%3D1398863696&ts=1398863718&use_mirror=superb-dca3 /tmp/nagios.tar.gz
+RUN cd /tmp && tar -zxvf nagios.tar.gz && cd nagios  && ./configure --prefix=${NAGIOS_HOME} --exec-prefix=${NAGIOS_HOME} --enable-event-broker --with-nagios-command-user=${NAGIOS_CMDUSER} --with-command-group=${NAGIOS_CMDGROUP} --with-nagios-user=${NAGIOS_USER} --with-nagios-group=${NAGIOS_GROUP} && make all && make install && make install-config && make install-commandmode && cp sample-config/httpd.conf /etc/apache2/conf.d/nagios.conf
+ADD http://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz /tmp/
+RUN cd /tmp && tar -zxvf nagios-plugins-1.5.tar.gz && cd nagios-plugins-1.5 && ./configure --prefix=${NAGIOS_HOME} && make && make install
+
+RUN sed -i.bak 's/.*\=www\-data//g' /etc/apache2/envvars
+RUN export DOC_ROOT="DocumentRoot $(echo $NAGIOS_HOME/share)"; sed -i "s,DocumentRoot.*,$DOC_ROOT," /etc/apache2/sites-available/default
+
+RUN ln -s ${NAGIOS_HOME}/bin/nagios /usr/local/bin/nagios && mkdir -p /usr/share/snmp/mibs && chmod 0755 /usr/share/snmp/mibs && touch /usr/share/snmp/mibs/.foo
+
+RUN echo "use_timezone=$NAGIOS_TIMEZONE" >> ${NAGIOS_HOME}/etc/nagios.cfg && echo "SetEnv TZ \"${NAGIOS_TIMEZONE}\"" >> /etc/apache2/conf.d/nagios.conf
+
+RUN mkdir -p ${NAGIOS_HOME}/etc/conf.d && mkdir -p ${NAGIOS_HOME}/etc/monitor && ln -s /usr/share/snmp/mibs ${NAGIOS_HOME}/libexec/mibs
+RUN echo "cfg_dir=${NAGIOS_HOME}/etc/conf.d" >> ${NAGIOS_HOME}/etc/nagios.cfg
+RUN echo "cfg_dir=${NAGIOS_HOME}/etc/monitor" >> ${NAGIOS_HOME}/etc/nagios.cfg
+RUN download-mibs && echo "mibs +ALL" > /etc/snmp/snmp.conf
+
+RUN sed -i 's,/bin/mail,/usr/bin/mail,' /opt/nagios/etc/objects/commands.cfg && \
+  sed -i 's,/usr/usr,/usr,' /opt/nagios/etc/objects/commands.cfg
+RUN cp /etc/services /var/spool/postfix/etc/
+
+RUN mkdir -p /etc/sv/nagios && mkdir -p /etc/sv/apache && rm -rf /etc/sv/getty-5 && mkdir -p /etc/sv/postfix
+ADD nagios.init /etc/sv/nagios/run
+ADD apache.init /etc/sv/apache/run
+ADD postfix.init /etc/sv/postfix/run
+ADD postfix.stop /etc/sv/postfix/finish
+
+ADD start.sh /usr/local/bin/start_nagios
+
+# install slack alert notification plugin
+ADD slack_nagios.cfg ${NAGIOS_HOME}/etc/conf.d/slack_nagios.cfg
+ADD slack_nagios.sh  /usr/local/bin/slack_nagios.sh
+RUN chmod +x /usr/local/bin/slack_nagios.sh
+
+ENV APACHE_LOCK_DIR /var/run
+ENV APACHE_LOG_DIR /var/log/apache2
+
+EXPOSE 80
+
+VOLUME /opt/nagios/var
+VOLUME /opt/nagios/etc
+VOLUME /opt/nagios/libexec
+VOLUME /var/log/apache2
+VOLUME /usr/share/snmp/mibs
+
+CMD ["/usr/local/bin/start_nagios"]
diff --git a/containers/nagios/Makefile b/containers/nagios/Makefile
new file mode 100644
index 0000000..0c6cb32
--- /dev/null
+++ b/containers/nagios/Makefile
@@ -0,0 +1,12 @@
+.PHONY: build
+	build: ; docker build --rm -t xosproject/nagios .
+
+.PHONY: run
+	run: ; docker run -d --name xosproject_nagios -p 8001:80 -t xosproject/nagios
+
+.PHONY: stop
+	stop: ; docker stop xosproject_nagios
+
+.PHONY: rm
+	rm: ; docker rm xosproject_nagios
+
diff --git a/containers/nagios/README.md b/containers/nagios/README.md
new file mode 100644
index 0000000..e6c2c0e
--- /dev/null
+++ b/containers/nagios/README.md
@@ -0,0 +1,14 @@
+## Docker-Nagios  [![Docker Build Status](http://72.14.176.28/cpuguy83/nagios)](https://registry.hub.docker.com/u/cpuguy83/nagios)
+
+Basic Docker image for running Nagios.<br />
+This is running Nagios 3.5.1
+
+You should either link a mail container in as "mail" or set MAIL_SERVER, otherwise
+mail will not work.
+
+### Knobs ###
+- NAGIOSADMIN_USER=nagiosadmin
+- NAGIOSAMDIN_PASS=nagios
+
+### Web UI ###
+The Nagios Web UI is available on port 80 of the container<br />
diff --git a/containers/nagios/apache.init b/containers/nagios/apache.init
new file mode 100755
index 0000000..9cb9a38
--- /dev/null
+++ b/containers/nagios/apache.init
@@ -0,0 +1,4 @@
+#!/bin/bash
+. /etc/default/apache2
+
+exec /usr/sbin/apache2 -D FOREGROUND
diff --git a/containers/nagios/nagios.init b/containers/nagios/nagios.init
new file mode 100755
index 0000000..30448f9
--- /dev/null
+++ b/containers/nagios/nagios.init
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exec ${NAGIOS_HOME}/bin/nagios ${NAGIOS_HOME}/etc/nagios.cfg
diff --git a/containers/nagios/postfix.init b/containers/nagios/postfix.init
new file mode 100755
index 0000000..29bf50b
--- /dev/null
+++ b/containers/nagios/postfix.init
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+: ${MAIL_SERVER=$MAIL_PORT_25_TCP_ADDR}
+
+
+sed -i "s/relayhost =.*/relayhost = ${MAIL_SERVER}/" /etc/postfix/main.cf
+sed -i "s/myhostname =.*/myhostname = `hostname`/" /etc/postfix/main.cf
+
+exec /usr/lib/postfix/master -d -c /etc/postfix
diff --git a/containers/nagios/postfix.stop b/containers/nagios/postfix.stop
new file mode 100755
index 0000000..50646e8
--- /dev/null
+++ b/containers/nagios/postfix.stop
@@ -0,0 +1 @@
+postfix stop
diff --git a/containers/nagios/slack_nagios.cfg b/containers/nagios/slack_nagios.cfg
new file mode 100644
index 0000000..8840c87
--- /dev/null
+++ b/containers/nagios/slack_nagios.cfg
@@ -0,0 +1,21 @@
+define contact {
+      contact_name                             slack
+      alias                                    Slack
+      service_notification_period              24x7
+      host_notification_period                 24x7
+      service_notification_options             w,u,c,r
+      host_notification_options                d,r
+      service_notification_commands            notify-service-by-slack
+      host_notification_commands               notify-host-by-slack
+}
+
+define command {
+      command_name     notify-service-by-slack
+      command_line     /usr/local/bin/slack_nagios.sh > /tmp/slack.log 2>&1
+}
+
+define command {
+      command_name     notify-host-by-slack
+      command_line     /usr/local/bin/slack_nagios.sh > /tmp/slack.log 2>&1
+}
+
diff --git a/containers/nagios/slack_nagios.sh b/containers/nagios/slack_nagios.sh
new file mode 100755
index 0000000..7c5e205
--- /dev/null
+++ b/containers/nagios/slack_nagios.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+# This script is used by Nagios to post alerts into a Slack channel
+# using the Incoming WebHooks integration. Create the channel, botname
+# and integration first and then add this notification script in your
+# Nagios configuration.
+#
+# All variables that start with NAGIOS_ are provided by Nagios as
+# environment variables when an notification is generated.
+# A list of the env variables is available here:
+#   http://nagios.sourceforge.net/docs/3_0/macrolist.html
+#
+# More info on Slack
+# Website: https://slack.com/
+# Twitter: @slackhq, @slackapi
+#
+# My info
+# Website: http://matthewcmcmillan.blogspot.com/
+# Twitter: @matthewmcmillan
+
+#Modify these variables for your environment
+MY_NAGIOS_HOSTNAME=""        # This server's hostname
+SLACK_HOSTNAME=""               
+SLACK_CHANNEL="#alerts"      
+SLACK_BOTNAME="nagios"
+WEBHOOK_URL=""               # Incomming webhook url for the slack account 
+
+#Set the message icon based on Nagios service state
+if [ "$NAGIOS_SERVICESTATE" = "CRITICAL" ]
+then
+    ICON=":exclamation:"
+elif [ "$NAGIOS_SERVICESTATE" = "WARNING" ]
+then
+    ICON=":warning:"
+elif [ "$NAGIOS_SERVICESTATE" = "OK" ]
+then
+    ICON=":white_check_mark:"
+elif [ "$NAGIOS_SERVICESTATE" = "UNKNOWN" ]
+then
+    ICON=":question:"
+else
+    ICON=":white_medium_square:"
+fi
+
+#Send message to Slack
+curl -X POST --data-urlencode "payload={\"channel\": \"${SLACK_CHANNEL}\", \"username\": \"${SLACK_USERNAME}\", \"text\": \"${ICON} HOST: ${MY_NAGIOS_HOSTNAME}   SERVICE: ${NAGIOS_SERVICEDISPLAYNAME}     MESSAGE: ${NAGIOS_SERVICEOUTPUT} <https://${MY_NAGIOS_HOSTNAME}/cgi-bin/nagios3/extinfo.cgi?type=1&host=${NAGIOS_HOSTNAME}|See Nagios>\"}" $WEBHOOK_URL
diff --git a/containers/nagios/start.sh b/containers/nagios/start.sh
new file mode 100755
index 0000000..f295e5b
--- /dev/null
+++ b/containers/nagios/start.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [ ! -f ${NAGIOS_HOME}/etc/htpasswd.users ] ; then
+  htpasswd -c -b -s ${NAGIOS_HOME}/etc/htpasswd.users ${NAGIOSADMIN_USER} ${NAGIOSADMIN_PASS}
+  chown -R nagios.nagios ${NAGIOS_HOME}/etc/htpasswd.users
+fi
+
+exec runsvdir /etc/sv
+
+/etc/init.d/apache2 start
diff --git a/views/ngXosLib/bower.json b/views/ngXosLib/bower.json
index 228a082..08a02e3 100644
--- a/views/ngXosLib/bower.json
+++ b/views/ngXosLib/bower.json
@@ -17,7 +17,6 @@
     "angular": "1.4.7",
     "angular-ui-router": "0.2.15",
     "angular-resource": "1.4.7",
-    "ng-lodash": "0.3.0",
     "angular-cookies": "1.4.7",
     "angular-animate": "1.4.7",
     "lodash": "~4.11.1",
diff --git a/views/ngXosLib/generator-xos/app/templates/gulp/build.js b/views/ngXosLib/generator-xos/app/templates/gulp/build.js
index 663a4cf..b66cdbc 100644
--- a/views/ngXosLib/generator-xos/app/templates/gulp/build.js
+++ b/views/ngXosLib/generator-xos/app/templates/gulp/build.js
@@ -98,8 +98,8 @@
   gulp.task('copyHtml', function(){
     return gulp.src(options.src + 'index.html')
       // remove dev dependencies from html
-      .pipe(replace(/<!-- bower:css -->(\n.*)*\n<!-- endbower --><!-- endcss -->/, ''))
-      .pipe(replace(/<!-- bower:js -->(\n.*)*\n<!-- endbower --><!-- endjs -->/, ''))
+      .pipe(replace(/<!-- bower:css -->(\n^<link.*)*\n<!-- endbower -->/gmi, ''))
+      .pipe(replace(/<!-- bower:js -->(\n^<script.*)*\n<!-- endbower -->/gmi, ''))
       // injecting minified files
       .pipe(
         inject(
@@ -150,6 +150,7 @@
   gulp.task('build', function() {
     runSequence(
       'clean',
+      'sass',
       'templates',
       'babel',
       'scripts',
diff --git a/views/ngXosLib/generator-xos/package.json b/views/ngXosLib/generator-xos/package.json
old mode 100755
new mode 100644
index 58b5a33..0098496
--- a/views/ngXosLib/generator-xos/package.json
+++ b/views/ngXosLib/generator-xos/package.json
@@ -4,7 +4,7 @@
   "description": "View generator for XOS",
   "main": "index.js",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "mocha test"
   },
   "author": "Matteo Scandolo",
   "license": "ISC",
@@ -13,5 +13,13 @@
   },
   "files": [
     "app"
-  ]
+  ],
+  "devDependencies": {
+    "mocha": "^2.4.5",
+    "mockery": "^1.7.0",
+    "rimraf": "^2.5.2",
+    "wiredep": "^4.0.0",
+    "yeoman-assert": "^2.2.1",
+    "yeoman-test": "^1.4.0"
+  }
 }
diff --git a/views/ngXosLib/generator-xos/test/build.spec.js b/views/ngXosLib/generator-xos/test/build.spec.js
new file mode 100644
index 0000000..dd49b03
--- /dev/null
+++ b/views/ngXosLib/generator-xos/test/build.spec.js
@@ -0,0 +1,182 @@
+'use strict';
+var path = require('path');
+var helpers = require('yeoman-test');
+var assert = require('yeoman-assert');
+var exec = require('child_process').exec;
+var fs = require('fs');
+const rimraf = require('rimraf');
+
+const getMillisec = min => min * 60 * 1000;
+const deleteFile = file => {
+  if(fs.existsSync(file)){
+    // console.log(`deleting: ${file}`);
+    fs.unlinkSync(file);
+  }
+}
+
+// source files
+const viewName = 'testDashboard';
+const fileName = viewName.replace(/^./, viewName[0].toUpperCase());
+const sourcePath = path.join(__dirname, `../../../ngXosViews/${viewName}/`);
+
+// dest files
+const basePath = '../../../../xos/core/xoslib';
+const destHtml = path.join(__dirname, basePath + '/dashboards/xosTestDashboard.html');
+const destJs = path.join(__dirname, basePath + '/static/js/xosTestDashboard.js');
+const destVendor = path.join(__dirname, basePath + '/static/js/vendor/xosTestDashboardVendor.js');
+const destCss = path.join(__dirname, basePath + '/static/css/xosTestDashboard.css');
+
+describe('The XOS Build script', function(){
+  const buildCmd = 'gulp build';
+  
+  this.timeout(getMillisec(5));
+
+  before(done => {
+    console.log('Running generator');
+    this.generator = helpers
+      .run(require.resolve('../app'))
+      .inDir(sourcePath)
+      .withOptions({ 'skip-install': false })
+      .withPrompts({
+        name: viewName,
+        host: 'test-host',
+        token: 'test-token',
+        session: 'test-session'
+      })
+      .on('end', () => {
+        process.stdout.write('Installing Node Modules');
+        let npmInstall = setInterval(() => {
+          process.stdout.write('.');
+        }, 1000);
+        exec('npm install', {
+          cwd: sourcePath
+        }, (err) => {
+          clearInterval(npmInstall);
+          process.stdout.write('\nInstalling Bower Components');
+          let bowerInstall = setInterval(() => {
+            process.stdout.write('.');
+          }, 1000);
+          exec('bower install', {
+            cwd: sourcePath
+          }, (err) => {
+            clearInterval(bowerInstall);
+            done(err);
+          });
+        });
+      });
+  });
+
+  describe('when no styles or vendors are added', () => {
+    
+    before((done) => {
+      process.stdout.write('\nBuilding App');
+      let appBuild = setInterval(() => {
+        process.stdout.write('.');
+      }, 1000);
+      exec(buildCmd, {
+        cwd: sourcePath
+      }, (err) => {
+        console.log(err);
+        clearInterval(appBuild);
+        done(err);
+      });
+    });
+
+    it('should have build the app', () => {
+      assert.file([destHtml, destJs]);
+    });
+
+    it('should include only minified files in the index', () => {
+      assert.fileContent(destHtml, `<script src="/static/js/xos${fileName}.js"></script>`);
+      assert.noFileContent(destHtml, `<!-- bower:css -->`);
+      assert.noFileContent(destHtml, `<!-- bower:js -->`);
+    });
+  });
+
+  describe('when a third party library is added', () => {
+    before((done) => {
+    process.stdout.write('\nInstalling 3rd party library');
+      let bowerInstall = setInterval(() => {
+        process.stdout.write('.');
+      }, 1000);
+      exec('bower install d3 --save', {
+        cwd: sourcePath
+      }, (err, out) => {
+        clearInterval(bowerInstall);
+        process.stdout.write('\nBuilding App');
+        let appBuild = setInterval(() => {
+          process.stdout.write('.');
+        }, 1000);
+        exec(buildCmd, {
+          cwd: sourcePath
+        }, (err) => {
+          console.log(err);
+          clearInterval(appBuild);
+          done(err);
+        }); 
+      });
+    });
+
+    it('should have build the app with a vendor file', () => {
+      assert.file([destHtml, destJs, destVendor]);
+    });
+
+    it('should include only minified files and minified deps in the index', () => {
+      assert.fileContent(destHtml, `<script src="/static/js/xos${fileName}.js"></script>`);
+      assert.fileContent(destHtml, `<script src="/static/js/vendor/xos${fileName}Vendor.js"></script>`);
+      assert.noFileContent(destHtml, `<!-- bower:css -->`);
+      assert.noFileContent(destHtml, `<!-- bower:js -->`);
+    });
+  });
+
+  describe('when some styles are added', () => {
+    before((done) => {
+      let styleContent = `
+        @import '../../../../style/sass/lib/_variables.scss';
+
+        #xosTestDashboard {
+          background: $brand-primary;
+        }
+      `;
+
+      fs.writeFile(`${sourcePath}src/sass/main.scss`, styleContent, function(err) {
+        process.stdout.write('\nBuilding the Application');
+        let appBuild = setInterval(() => {
+          process.stdout.write('.');
+        }, 1000);
+        exec('bower uninstall d3 --save', {
+          cwd: sourcePath
+        }, (err, out) => {
+          exec(buildCmd, {
+            cwd: sourcePath
+          }, (err, out) => {
+            clearInterval(appBuild);
+            done();
+          })
+        })
+      });
+    });
+
+    it('should have build the app with a css file', () => {
+      assert.file([destHtml, destJs, destCss]);
+    });
+
+    it('should include only minified files and minified deps in the index', () => {
+      assert.fileContent(destHtml, `<script src="/static/js/xos${fileName}.js"></script>`);
+      assert.fileContent(destHtml, `<link rel="stylesheet" href="/static/css/xos${fileName}.css">`);
+      assert.noFileContent(destHtml, `<!-- bower:css -->`);
+      assert.noFileContent(destHtml, `<!-- bower:js -->`);
+
+      assert.fileContent(destCss, `background:#337ab7`);
+    });
+  });
+
+  after(done => {
+    // deleting the folder used for test
+    deleteFile(destHtml);
+    deleteFile(destJs);
+    deleteFile(destVendor);
+    deleteFile(destCss);
+    rimraf(sourcePath, {}, done);
+  });
+});
\ No newline at end of file
diff --git a/views/ngXosLib/generator-xos/test/generator.spec.js b/views/ngXosLib/generator-xos/test/generator.spec.js
new file mode 100644
index 0000000..f75f444
--- /dev/null
+++ b/views/ngXosLib/generator-xos/test/generator.spec.js
@@ -0,0 +1,117 @@
+'use strict';
+
+const path = require('path');
+const helpers = require('yeoman-test');
+const assert = require('yeoman-assert');
+const rimraf = require('rimraf');
+const mockery = require('mockery');
+const wiredep = require('wiredep');
+
+const firstCharTouppercase = string => string.replace(/^./, string[0].toUpperCase())
+
+// get bower deps installed in ngXosLib
+let bowerDeps = wiredep({
+  cwd: path.join(__dirname, '../../'), // pretending to be in the ngXosLib root
+  exclude: ['Chart.js']
+});
+bowerDeps = bowerDeps.js.map(d => d.match(/bower_components\/([a-zA-Z\-`.]+)\//)[1]);
+
+// test values
+const viewName = 'testDashboard';
+const fileName = firstCharTouppercase(viewName);
+const testPath = path.join(__dirname, `../../../ngXosViews/${viewName}/`);
+
+const getDefaultFiles = () => {
+  return [
+    '.bowerrc',
+    '.eslintrc',
+    '.gitignore',
+    'bower.json',
+    'gulpfile.js',
+    'karma.conf.js',
+    'package.json',
+    'src/index.html',
+  ].map(i => `${testPath}${i}`);
+};
+
+const yeomanUserMock = {
+  git: {
+    name: () => 'Test User',
+    email: () => 'test@mail.org'
+  }
+}
+
+mockery.enable({
+  warnOnReplace: false,
+  warnOnUnregistered: false,
+  useCleanCache: true,
+});
+mockery.resetCache();
+mockery.registerMock('../node_modules/yeoman-generator/lib/actions/user', yeomanUserMock);
+
+describe('Yeoman XOS generator', function () {
+
+  beforeEach(() => {
+  });
+
+  before(done => {
+    this.generator = helpers
+      .run(require.resolve('../app'))
+      .inDir(testPath)
+      .withOptions({ 'skip-install': true })
+      .withPrompts({
+        name: viewName,
+        host: 'test-host',
+        token: 'test-token',
+        session: 'test-session'
+      })
+      .on('end', done);
+  });
+
+
+  it('should generate base files in the correct directory', () => {
+    assert.file(getDefaultFiles());
+  });
+
+  it('should create the env file with correct params', () => {
+    assert.fileContent(`${testPath}env/default.js`, 'host: \'test-host\'');
+    assert.fileContent(`${testPath}env/default.js`, 'xoscsrftoken: \'test-token\'');
+    assert.fileContent(`${testPath}env/default.js`, 'xossessionid: \'test-session\'');
+  });
+
+  it('should write username in package & bower json', () => {
+    assert.fileContent(`${testPath}package.json`, '"author": "Test User"');
+    assert.fileContent(`${testPath}bower.json`, '"Test User <test@mail.org>"')
+  });
+
+  it('should add all xosLib dependencies in the dev section of bower.json', () => {
+    bowerDeps.forEach(d => {
+      assert.fileContent(`${testPath}bower.json`, d);
+    });
+  });
+
+  it('should set the right module name in all the files', () => {
+    assert.fileContent(`${testPath}src/index.html`, `ng-app="xos.${viewName}"`)
+    assert.fileContent(`${testPath}src/index.html`, `id="xos${fileName}"`)
+    assert.fileContent(`${testPath}src/js/main.js`, `angular.module('xos.${viewName}', [`)
+    assert.fileContent(`${testPath}src/sass/main.scss`, `#xos${fileName}`)
+  });
+
+  it('should set correct paths in build file', () => {
+    assert.fileContent(`${testPath}gulp/build.js`, `angular.module('xos.${viewName}')`)
+    assert.fileContent(`${testPath}gulp/build.js`, `options.dashboards + 'xos${fileName}.html'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `options.static + 'css/xos${fileName}.css'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `.pipe(concat('xos${fileName}.css'))`)
+    assert.fileContent(`${testPath}gulp/build.js`, `.pipe(concat('xos${fileName}.js'))`)
+    assert.fileContent(`${testPath}gulp/build.js`, `module: 'xos.${viewName}'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `options.static + 'js/vendor/xos${fileName}Vendor.js'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `options.static + 'js/xos${fileName}.js'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `options.static + 'css/xos${fileName}.css'`)
+    assert.fileContent(`${testPath}gulp/build.js`, `.pipe(concat('xos${fileName}Vendor.js'))`)
+  });
+
+  after(done => {
+    // deleting the folder used for test
+    rimraf(testPath, {}, done)
+  });
+});
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/js/main.js b/views/ngXosViews/serviceGrid/src/js/main.js
index 5c41bd4..c52ef9f 100644
--- a/views/ngXosViews/serviceGrid/src/js/main.js
+++ b/views/ngXosViews/serviceGrid/src/js/main.js
@@ -60,6 +60,10 @@
       .then((services) => {
         this.services = _.map(services, s => {
           // parse backend_status string in a boolean for display
+          // NOTE they are not boolean:
+          // - start with 0 = provisioning
+          // - start with 1 = good
+          // - start with 2 = error
           s.status = parseInt(s.backend_status.match(/^[0-9]/)[0]) === 0 ? false : true;
           return s;
         })
diff --git a/xos/configurations/cord-pod/README-Tutorial.md b/xos/configurations/cord-pod/README-Tutorial.md
index 805812f..94f5170 100644
--- a/xos/configurations/cord-pod/README-Tutorial.md
+++ b/xos/configurations/cord-pod/README-Tutorial.md
@@ -5,29 +5,27 @@
 
 ## Prepare the development POD
 
-Follow steps 1-3 under the **How to Bring up CORD** heading in the
-[README.md](./README.md) file.  For best results, use on a clean Ubuntu 14.04
+This tutorial runs on a single-node CORD POD development environment.
+For best results, prepare a clean Ubuntu 14.04
 LTS installation on a server with at least 48GB RAM and 12 CPU cores.
+Update the packages to the latest versions.
 
-For step 1, use the single-node POD setup described at
-https://github.com/open-cloud/openstack-cluster-setup.  If you like, you can run
-[this script](https://github.com/open-cloud/openstack-cluster-setup/blob/master/scripts/single-node-pod.sh) to perform steps 1 and 2:
+To set up the POD, run
+[this script](https://github.com/open-cloud/openstack-cluster-setup/blob/master/scripts/single-node-pod.sh)
+with the `-e` option:
 
 ```
 ubuntu@pod:~$ wget https://raw.githubusercontent.com/open-cloud/openstack-cluster-setup/master/scripts/single-node-pod.sh
-ubuntu@pod:~$ bash single-node-pod.sh
+ubuntu@pod:~$ bash single-node-pod.sh -e
 ```
 
-For step 3, in place of the `compute-ext-net.sh` script, run
-[this script](https://github.com/open-cloud/openstack-cluster-setup/blob/master/scripts/compute-ext-net-tutorial.sh)
-inside the nova-compute VM.  It enables routing packets between the ExampleService and vSG subnets on a
-single-node POD.
+> NOTE: The above script can also automatically perform (nearly) all the steps of this
+> tutorial if run as `bash single-node-pod -e -t`.  However, you will still need 
+> to manually log into XOS and create an ExampleTenant, as described under 
+> [Configure ExampleService in XOS](#configure-exampleservice-in-xos)
+> below.  The script will tell you when it's time to do this.
 
-```
-ubuntu@pod:~$ ssh ubuntu@nova-compute
-ubuntu@nova-compute:~$ wget https://raw.githubusercontent.com/open-cloud/openstack-cluster-setup/master/scripts/compute-ext-net-tutorial.sh
-ubuntu@nova-compute:~$ sudo bash compute-ext-net-tutorial.sh
-```
+Be patient... it will take at least one hour to fully set up the single-node POD.
 
 ## Include ExampleService in XOS
 
@@ -37,15 +35,14 @@
 Change the XOS code as described in the
 [ExampleService Tutorial](http://guide.xosproject.org/devguide/exampleservice/)
 under the **Install the Service in Django** heading, and rebuild the XOS containers as
-described in that Tutorial:
+follows:
 
 ```
-ubuntu@xos:~$ cd xos/xos/configurations/devel
-ubuntu@xos:~/xos/xos/configurations/devel$ make containers
+ubuntu@xos:~$ cd xos/xos/configurations/cord-pod
+ubuntu@xos:~/xos/xos/configurations/cord-pod$ make local_containers
 ```
 
-Change directories to `../cord-pod`.  
-Modify the `docker-compose.yml` file in this directory to include the synchronizer
+Modify the `docker-compose.yml` file in the `cord-pod` directory to include the synchronizer
 for ExampleService:
 
 ```yaml
@@ -65,7 +62,7 @@
 
 Also, add ExampleService's public key to the `volumes` section of the `xos` docker container:
 
-```
+```yaml
 xos:
     ...
     volumes:
@@ -75,21 +72,8 @@
 
 ## Bring up XOS
 
-Run the `make` commands described in the [README.md](./README.md) file:
-
-```
-ubuntu@xos:~/xos/xos/configurations/cord-pod$ make
-ubuntu@xos:~/xos/xos/configurations/cord-pod$ make vtn
-ubuntu@xos:~/xos/xos/configurations/cord-pod$ make cord
-```
-
-The first `make` command initializes XOS and configures it to talk to OpenStack.
-After running it you should be able to login to the XOS UI at http://xos
-using credentials padmin@vicci.org/letmein.
-
-The `make vtn` tells XOS to start and configure the ONOS VTN app.  The `make cord`
-installs the CORD services in XOS and configures a sample subscriber; the end
-result is that XOS will spin up the subscriber's vSG.
+Run the `make` commands described in the [Bringing up XOS](https://github.com/open-cloud/xos/blob/master/xos/configurations/cord-pod/README.md#bringing-up-xos)
+section of the README.md file.
 
 ## Configure ExampleService in XOS
 
@@ -100,10 +84,9 @@
 ubuntu@xos:~/xos/xos/configurations/cord-pod$ make exampleservice
 ```
 
-In the XOS UI, create an ExampleTenant. Go to *http://xos/admin/exampleservice*
-and add / save an Example Tenant (when creating the tenant, fill in a message that
-this tenant should display).  This will cause an Instance to be created
-in the the *mysite_exampleservice* slice.
+This will add the ExampleService to XOS.  It will also create an ExampleTenant,
+which causes a VM to be created with Apache running inside.
+
 
 ## Set up a Subscriber Device
 
diff --git a/xos/configurations/cord-pod/README.md b/xos/configurations/cord-pod/README.md
index d5051c9..8813d3e 100644
--- a/xos/configurations/cord-pod/README.md
+++ b/xos/configurations/cord-pod/README.md
@@ -50,7 +50,7 @@
 The CORD fabric is responsible for providing external (Internet) connectivity
 for VMs created on CORD.  If you are running on CloudLab (or another development
 environment) and want external connectivity without the fabric, download [this script](https://raw.githubusercontent.com/open-cloud/openstack-cluster-setup/master/scripts/compute-ext-net.sh)
- and run it as root:
+ and run it on the Nova compute node(s) as root:
  ```
  $ sudo compute-ext-net.sh
  ```
@@ -87,11 +87,9 @@
 ubuntu@xos:~/xos/xos/configurations/cord-pod$ make
 ```
 
-After this you will be able to login to the XOS GUI at
-*http://xos/* using username/password `padmin@vicci.org/letmein`.
 Before proceeding, you should verify that objects in XOS are
-being sync'ed with OpenStack.  Log into the GUI and select *Users*
-at left.  Make sure there is a green check next to `padmin@vicci.org`.
+being sync'ed with OpenStack. [Login to the XOS GUI](#logging-into-xos-on-cloudlab-or-any-remote-host) 
+and select *Users* at left.  Make sure there is a green check next to `padmin@vicci.org`.
 
 > If you are **not** building the single-node development POD, the next
 > step is to create and edit the VTN configuration.  Run `make vtn-external.yaml`
@@ -112,7 +110,8 @@
 and pushing it to ONOS.  You are able to see and modify the configuration
 via the GUI as follows:
 
-* To see the generated configuration, go to *http://xos/admin/onos/onosapp/*, select
+* To see the generated configuration, go to *http://xos/admin/onos/onosapp/* 
+([caveat](#logging-into-xos-on-cloudlab-or-any-remote-host)), select
 *VTN_ONOS_app*, then the *Attributes* tab, and look for the
 `rest_onos/v1/network/configuration/` attribute.  
 
@@ -182,3 +181,20 @@
 CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS               NAMES
 2b0bfb3662c7        andybavier/docker-vcpe   "/sbin/my_init"     5 days ago          Up 5 days                               vcpe-222-111
 ```
+
+### Logging into XOS on CloudLab (or any remote host)
+
+The XOS service is accessible on the POD at `http://xos/`, but `xos` maps to a private IP address
+on the management network.  If you install CORD on CloudLab 
+you will not be able to directly access the XOS GUI.
+In order to log into the XOS GUI in the browser on your local machine (desktop or laptop), 
+you can set up an SSH tunnel to your CloudLab node.  Assuming that 
+`<your-cloudlab-node>` is the DNS name of the CloudLab node hosting your experiment,
+run the following on your local machine to create the tunnel:
+
+```
+$ ssh -L 8888:xos:80 <your-cloudlab-node>
+```
+
+Then you should be able to access the XOS GUI by pointing your browser to
+`http://localhost:8888`.  Default username/password is `padmin@vicci.org/letmein`.
diff --git a/xos/configurations/cord-pod/pod-exampleservice.yaml b/xos/configurations/cord-pod/pod-exampleservice.yaml
index ec45bb8..23add83 100644
--- a/xos/configurations/cord-pod/pod-exampleservice.yaml
+++ b/xos/configurations/cord-pod/pod-exampleservice.yaml
@@ -4,6 +4,7 @@
 
 imports:
    - custom_types/xos.yaml
+   - custom_types/exampleservice.yaml
 
 topology_template:
   node_templates:
@@ -73,6 +74,15 @@
           kind: exampleservice
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
           private_key_fn: /opt/xos/synchronizers/exampleservice/exampleservice_private_key
+          service_message: hello
       artifacts:
           pubkey: /opt/xos/synchronizers/exampleservice/exampleservice_public_key
 
+    tenant#exampletenant1:
+        type: tosca.nodes.ExampleTenant
+        properties:
+            tenant_message: world
+        requirements:
+          - tenant:
+              node: service#exampleservice
+              relationship: tosca.relationships.TenantOfService
diff --git a/xos/tosca/custom_types/exampleservice.m4 b/xos/tosca/custom_types/exampleservice.m4
new file mode 100644
index 0000000..720913e
--- /dev/null
+++ b/xos/tosca/custom_types/exampleservice.m4
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# compile this with "m4 exampleservice.m4 > exampleservice.yaml"
+
+# include macros
+include(macros.m4)
+
+node_types:
+    tosca.nodes.ExampleService:
+        derived_from: tosca.nodes.Root
+        description: >
+            Example Service
+        capabilities:
+            xos_base_service_caps
+        properties:
+            xos_base_props
+            xos_base_service_props
+            service_message:
+                type: string
+                required: false
+
+    tosca.nodes.ExampleTenant:
+        derived_from: tosca.nodes.Root
+        description: >
+            A Tenant of the example service
+        properties:
+            xos_base_tenant_props
+            tenant_message:
+                type: string
+                required: false
+
diff --git a/xos/tosca/custom_types/exampleservice.yaml b/xos/tosca/custom_types/exampleservice.yaml
new file mode 100644
index 0000000..2cd70dd
--- /dev/null
+++ b/xos/tosca/custom_types/exampleservice.yaml
@@ -0,0 +1,101 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# compile this with "m4 exampleservice.m4 > exampleservice.yaml"
+
+# include macros
+# Note: Tosca derived_from isn't working the way I think it should, it's not
+#    inheriting from the parent template. Until we get that figured out, use
+#    m4 macros do our inheritance
+
+
+# Service
+
+
+# Subscriber
+
+
+
+
+# end m4 macros
+
+
+
+node_types:
+    tosca.nodes.ExampleService:
+        derived_from: tosca.nodes.Root
+        description: >
+            Example Service
+        capabilities:
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.
+            service_message:
+                type: string
+                required: false
+
+    tosca.nodes.ExampleTenant:
+        derived_from: tosca.nodes.Root
+        description: >
+            A Tenant of the example service
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service
+            tenant_message:
+                type: string
+                required: false
+
diff --git a/xos/tosca/custom_types/macros.m4 b/xos/tosca/custom_types/macros.m4
new file mode 100644
index 0000000..1f48f10
--- /dev/null
+++ b/xos/tosca/custom_types/macros.m4
@@ -0,0 +1,84 @@
+# Note: Tosca derived_from isn't working the way I think it should, it's not
+#    inheriting from the parent template. Until we get that figured out, use
+#    m4 macros do our inheritance
+
+define(xos_base_props,
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object)
+# Service
+define(xos_base_service_caps,
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service)
+define(xos_base_service_props,
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.)
+# Subscriber
+define(xos_base_subscriber_caps,
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber)
+define(xos_base_subscriber_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of subscriber
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+define(xos_base_tenant_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+
+# end m4 macros
+
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index e83a22f..28a4205 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -1,90 +1,9 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
-# Note: Tosca derived_from isn't working the way I think it should, it's not
-#    inheriting from the parent template. Until we get that figured out, use
-#    m4 macros do our inheritance
+# compile this with "m4 xos.m4 > xos.yaml"
 
-define(xos_base_props,
-            no-delete:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to delete this object
-            no-create:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to create this object
-            no-update:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to update this object
-            replaces:
-                type: string
-                required: false
-                descrption: Replaces/renames this object)
-# Service
-define(xos_base_service_caps,
-            scalable:
-                type: tosca.capabilities.Scalable
-            service:
-                type: tosca.capabilities.xos.Service)
-define(xos_base_service_props,
-            kind:
-                type: string
-                default: generic
-                description: Type of service.
-            view_url:
-                type: string
-                required: false
-                description: URL to follow when icon is clicked in the Service Directory.
-            icon_url:
-                type: string
-                required: false
-                description: ICON to display in the Service Directory.
-            enabled:
-                type: boolean
-                default: true
-            published:
-                type: boolean
-                default: true
-                description: If True then display this Service in the Service Directory.
-            public_key:
-                type: string
-                required: false
-                description: Public key to install into Instances to allows Services to SSH into them.
-            private_key_fn:
-                type: string
-                required: false
-                description: Location of private key file
-            versionNumber:
-                type: string
-                required: false
-                description: Version number of Service.)
-# Subscriber
-define(xos_base_subscriber_caps,
-            subscriber:
-                type: tosca.capabilities.xos.Subscriber)
-define(xos_base_subscriber_props,
-            kind:
-                type: string
-                default: generic
-                description: Kind of subscriber
-            service_specific_id:
-                type: string
-                required: false
-                description: Service specific ID opaque to XOS but meaningful to service)
-define(xos_base_tenant_props,
-            kind:
-                type: string
-                default: generic
-                description: Kind of tenant
-            service_specific_id:
-                type: string
-                required: false
-                description: Service specific ID opaque to XOS but meaningful to service)
-
-# end m4 macros
-#
-# compile this with "m4 custom_types/xos.m4 > custom_types/xos.yaml"
+# include macros
+include(macros.m4)
 
 node_types:
     tosca.nodes.Service:
@@ -317,17 +236,6 @@
             xos_base_props
             xos_base_service_props
 
-    tosca.nodes.ExampleService:
-        derived_from: tosca.nodes.Root
-        description: >
-            Example Service
-        capabilities:
-            xos_base_service_caps
-        properties:
-            xos_base_props
-            xos_base_service_props
-
-
     tosca.nodes.Subscriber:
         derived_from: tosca.nodes.Root
         description: XOS subscriber base class.
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index b41a6c7..d83ca09 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -1,5 +1,8 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
+# compile this with "m4 xos.m4 > xos.yaml"
+
+# include macros
 # Note: Tosca derived_from isn't working the way I think it should, it's not
 #    inheriting from the parent template. Until we get that figured out, use
 #    m4 macros do our inheritance
@@ -14,8 +17,8 @@
 
 
 # end m4 macros
-#
-# compile this with "m4 custom_types/xos.m4 > custom_types/xos.yaml"
+
+
 
 node_types:
     tosca.nodes.Service:
@@ -667,65 +670,6 @@
                 required: false
                 description: Version number of Service.
 
-    tosca.nodes.ExampleService:
-        derived_from: tosca.nodes.Root
-        description: >
-            Example Service
-        capabilities:
-            scalable:
-                type: tosca.capabilities.Scalable
-            service:
-                type: tosca.capabilities.xos.Service
-        properties:
-            no-delete:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to delete this object
-            no-create:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to create this object
-            no-update:
-                type: boolean
-                default: false
-                description: Do not allow Tosca to update this object
-            replaces:
-                type: string
-                required: false
-                descrption: Replaces/renames this object
-            kind:
-                type: string
-                default: generic
-                description: Type of service.
-            view_url:
-                type: string
-                required: false
-                description: URL to follow when icon is clicked in the Service Directory.
-            icon_url:
-                type: string
-                required: false
-                description: ICON to display in the Service Directory.
-            enabled:
-                type: boolean
-                default: true
-            published:
-                type: boolean
-                default: true
-                description: If True then display this Service in the Service Directory.
-            public_key:
-                type: string
-                required: false
-                description: Public key to install into Instances to allows Services to SSH into them.
-            private_key_fn:
-                type: string
-                required: false
-                description: Location of private key file
-            versionNumber:
-                type: string
-                required: false
-                description: Version number of Service.
-
-
     tosca.nodes.Subscriber:
         derived_from: tosca.nodes.Root
         description: XOS subscriber base class.
diff --git a/xos/tosca/destroy.py b/xos/tosca/destroy.py
index d7f8300..6fa9101 100644
--- a/xos/tosca/destroy.py
+++ b/xos/tosca/destroy.py
@@ -7,9 +7,6 @@
 parentdir = os.path.dirname(currentdir)
 sys.path.append(parentdir)
 
-# a bit of a hack for developing -- run m4 to generate xos.yaml from xos.m4
-os.system("m4 %s/custom_types/xos.m4 > %s/custom_types/xos.yaml" % (currentdir, currentdir))
-
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
 import django
 django.setup()
diff --git a/xos/tosca/makedocs.py b/xos/tosca/makedocs.py
index 6a9e959..2db6db8 100644
--- a/xos/tosca/makedocs.py
+++ b/xos/tosca/makedocs.py
@@ -10,9 +10,6 @@
 parentdir = os.path.dirname(currentdir)
 sys.path.append(parentdir)
 
-# a bit of a hack for developing -- run m4 to generate xos.yaml from xos.m4
-os.system("m4 %s/custom_types/xos.m4 > %s/custom_types/xos.yaml" % (currentdir, currentdir))
-
 """
 {'derived_from': 'tosca.nodes.Root', 'capabilities': {'scalable': {'type': 'tosca.capabilities.Scalable'},
 'service': {'type': 'tosca.capabilities.xos.Service'}}, 'properties': {'icon_url': {'required': False,
diff --git a/xos/tosca/resources/exampleservice.py b/xos/tosca/resources/exampleservice.py
index 9d41807..f26b8b7 100644
--- a/xos/tosca/resources/exampleservice.py
+++ b/xos/tosca/resources/exampleservice.py
@@ -14,7 +14,7 @@
 class XOSExampleService(XOSResource):
     provides = "tosca.nodes.ExampleService"
     xos_model = ExampleService
-    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "private_key_fn", "versionNumber"]
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "private_key_fn", "versionNumber", "service_message"]
 
     def postprocess(self, obj):
         for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
diff --git a/xos/tosca/resources/exampletenant.py b/xos/tosca/resources/exampletenant.py
new file mode 100644
index 0000000..d9239f9
--- /dev/null
+++ b/xos/tosca/resources/exampletenant.py
@@ -0,0 +1,36 @@
+import importlib
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+from core.models import Tenant, Service
+from services.exampleservice.models import ExampleTenant, SERVICE_NAME as EXAMPLETENANT_KIND
+
+from xosresource import XOSResource
+
+class XOSExampleTenant(XOSResource):
+    provides = "tosca.nodes.ExampleTenant"
+    xos_model = ExampleTenant
+    name_field = "service_specific_id"
+    copyin_props = ("tenant_message",)
+
+    def get_xos_args(self, throw_exception=True):
+        args = super(XOSExampleTenant, self).get_xos_args()
+
+        # ExampleTenant must always have a provider_service
+        provider_name = self.get_requirement("tosca.relationships.TenantOfService", throw_exception=True)
+        if provider_name:
+            args["provider_service"] = self.get_xos_object(Service, throw_exception=True, name=provider_name)
+
+        return args
+
+    def get_existing_objs(self):
+        args = self.get_xos_args(throw_exception=False)
+        return ExampleTenant.get_tenant_objects().filter(provider_service=args["provider_service"], service_specific_id=args["service_specific_id"])
+        return []
+
+    def can_delete(self, obj):
+        return super(XOSExampleTenant, self).can_delete(obj)
+
diff --git a/xos/tosca/run.py b/xos/tosca/run.py
index 58dc22b..0ba2df9 100644
--- a/xos/tosca/run.py
+++ b/xos/tosca/run.py
@@ -7,9 +7,6 @@
 parentdir = os.path.dirname(currentdir)
 sys.path.append(parentdir)
 
-# a bit of a hack for developing -- run m4 to generate xos.yaml from xos.m4
-os.system("m4 %s/custom_types/xos.m4 > %s/custom_types/xos.yaml" % (currentdir, currentdir))
-
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
 import django
 django.setup()
diff --git a/xos/tosca/samples/exampleservice.yaml b/xos/tosca/samples/exampleservice.yaml
new file mode 100644
index 0000000..5b90ce2
--- /dev/null
+++ b/xos/tosca/samples/exampleservice.yaml
@@ -0,0 +1,47 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup the ExampleService
+
+imports:
+   - custom_types/xos.yaml
+   - custom_types/exampleservice.yaml
+
+topology_template:
+  node_templates:
+
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_exampleservice:
+      description: This slice holds the ExampleService
+      type: tosca.nodes.Slice
+
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+          - exmapleserver:
+              node: service#exampleservice
+              relationship: tosca.relationships.MemberOfService
+
+    service#exampleservice:
+      type: tosca.nodes.ExampleService
+      properties:
+          view_url: /admin/exampleservice/exampleservice/$id$/
+          kind: exampleservice
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          private_key_fn: /opt/xos/synchronizers/exampleservice/exampleservice_private_key
+          service_message: hello
+      artifacts:
+          pubkey: /opt/xos/synchronizers/exampleservice/exampleservice_public_key
+
+
+    tenant#exampletenant1:
+        type: tosca.nodes.ExampleTenant
+        properties:
+            tenant_message: world
+        requirements:
+          - tenant:
+              node: service#exampleservice
+              relationship: tosca.relationships.TenantOfService
+