Merge "[CORD-1947] veestat required packages" into cord-4.1
diff --git a/docs/developer/gui_extensions.md b/docs/developer/gui_extensions.md
index b299283..4bb6108 100644
--- a/docs/developer/gui_extensions.md
+++ b/docs/developer/gui_extensions.md
@@ -1,23 +1,45 @@
-# Create a custom GUI Extension
+# Creating a custom GUI Extension
  
 The CORD GUI is designed to be extensible. There are two cases in which we envision an extension to be needed:
 - Provide a different view over data
 - Create custom interfaces for services
  
-The development process for the two cases it's absolutely the same.
+The development process for both is the same.
   
-We suggest to get started by duplicating [xos-sample-gui-extension](https://github.com/opencord/xos-sample-gui-extension)
-  
-## Development
- 
-The development environment is the same as `xos-gui` so you can use the `npm start` command and augment it with the same sets fo environment variables.
-The dev server is going to proxy your requests to the appropriate backend and load the base application from it.
+## Starting Development 
+
+### Option 1: Use the provided Yeoman xos-gui-extension generator
+
+The provided generator in `generator-xos-gui-extension` will generate a new GUI extension for you with the name of
+your choice and demo components based on a simplified `sample-gui-extension`. No refactoring necessary.
+
+#### Prerequisites
+You must have the Yeoman toolset installed via npm on your system. It can be installed by running `npm install --global yo`. 
+If you encounter any issues, full detailed instructions on installation can be found at the [Yeoman website](http://yeoman.io/codelab/setup.html).
+
+#### Installation
+Once you have successfully installed Yeoman, run the following to install the generator.
+```bash
+cd ~cord/orchestration/xos-gui/generator-xos-gui-extension
+npm link
+```
+To run the generator, simply run `yo xos-gui-extension` from whatever location in your file system you wish to place your
+new GUI extension. The extension will prompt for a name for your extension. 
+
+
+### Option 2: Copy over sample-gui-extension
+If you choose not to use the Yeoman generator, you can copy over the contents of `sample-gui-extension` to your desired
+destination in your file system. If you are creating a GUI extension to used with a service, we suggest creating the 
+extension in a folder named `gui` in the service's `xos` folder as follows: `orchestration/xos_services/service_name/xos/gui/`.
+When changing the name of `sample-gui-extension`, you must be wary to change all instances of `sample-gui-extension` in the 
+extension folder.
+
  
 ## Add an extension to a profile
  
-To deploy your GUI extension with a cord profile you'll need to reference it in `platform-install`.
+To deploy your GUI extension with a cord profile you'll need to reference it in `platform-install/profile-manifests`.
  
-Open the `profile-manifest` you're working on (eg: profile_manifests/frontend.yml) and locate `enabled_gui_extensions`.
+Open the `profile-manifest` you're working on (eg: `profile_manifests/ecord-global.yml`) and locate `enabled_gui_extensions`.
 It may appear in two forms, depending whether or not there are others loaded extensions:
 ```yaml
 enabled_gui_extensions:
@@ -28,7 +50,7 @@
 ```yaml
 enabled_gui_extensions: []
 ```
-_NOTE: if it is not there, just create it_
+_NOTE: if it is not there, just create it._
 
 To add your extension, just add it to the list:
 ```yaml
@@ -40,7 +62,7 @@
 ```
 _NOTE: if it was defined as an empty array you'll need to remove the square brackets (`[]`)_
 
-The `name` field must match the directory in which the GUI Extension is built. You can update it in `conf/gulp.conf.js`, just locate:
+You must make sure that the `name` field matches the directory in which the GUI Extension is built. You can update it in `conf/gulp.conf.js`.
 ```js
 exports.paths = {
   src: 'src',
@@ -51,6 +73,97 @@
   tasks: 'gulp_tasks'
 };
 ```
-and replace `sample` with the appropriate name.
+and replace `sample` with your appropriate name. If you used the Yeoman generator, `sample` will already have been 
+replaced with the GUI extension name you chose.
 
-The `path` field identify the directory (starting from the `repo` root), in which your extension is stored. As now is not supported the loading from external sources. 
\ No newline at end of file
+The `path` field identifies the directory (starting from the CORD `repo` root), in which your extension is stored.
+Loading from external sources is not currently supported.
+
+## Additional Tips
+
+### Including Extra Files
+
+Additional necessary files (such as stylesheets or config files) can be added to the profile manifest as follows, 
+with the extension's `src` folder as the root. Here, we use `xos-sample-gui-extension` as an example.
+
+```yaml
+enabled_gui_extensions:
+  - name: sample
+    path: orchestration/xos-sample-gui-extension
+    extra_files:
+      -  app/style/style.css
+```
+
+### Generating config files
+
+During development, you may find it necessary to create separate config files in order to include other files used in
+your extension (such as images). The path to your extension may vary depending on whether you are running it locally 
+(`./xos/extensions/extension-name`) vs. on a container in production (`./extensions/extension-name`).
+
+You can create separate `customconfig.local.js` and `customconfig.production.js` files in the `conf/` folder, and then edit the 
+following portion of the appropriate `webpack.conf.js` file as follows:
+
+```js
+new CopyWebpackPlugin([
+      { from: `./conf/app/app.config.${env}.js`, to: `app.config.js` },
+      { from: `./conf/app/style.config.${brand}.js`, to: `style.config.js` },
+      // add your file here
+      { from: `./conf/app/customconfig.local.js`, to: `customconfig.js`}
+    ])
+```
+
+`webpack.conf.js` will be used in a local development environment, such as when running `npm start`
+
+`webpack-dist.conf.js` will be used in a production container after deploying a profile.
+
+### Handy XOS Components and Services
+
+The following XOS components and services may be helpful to you in your GUI extension development.
+
+#### XosComponentInjector
+Allows for the injection of components into the XOS GUI by specifying a target element ID. Useful IDs include:
+* `#dashboard-component-container`: the dashboard as seen on the XOS home
+* `#side-panel-container`: a side panel that can slide out from the right. However, there is also a `XosSidePanel` 
+service that can make development easier.
+
+#### XosConfirm
+Allows for the creation of confirmation modal dialogs to confirm whether or not to execute a selected action.
+
+#### XosKeyboardShortcut
+Allows for the creation of custom user keyboard shortcuts. See the provided `components/demo.ts` as an example.
+
+#### XosModelStore
+Provides easy access to model ngResources provided by an XOS service. Can be used as follows in your component's
+associated TypeScript file:
+
+```typescript
+import {Subscription} from 'rxjs/Subscription';
+export class ExampleComponent {
+    static $inject = ['XosModelStore'];
+    public resource;
+    private modelSubscription : Subscription;
+    constructor(
+      private XosModelStore: any,
+    ){}
+    
+    $onInit() {
+        this.modelSubscription = this.XosModelStore.query('SampleModel', '/sampleservice/SampleModels').subscribe(
+          res => {
+            this.resource = res;
+          }
+        );
+    }
+}
+export const exampleComponent : angular.IComponentOptions = {
+  template: require('./example.component.html'),
+  controllerAs: 'vm',
+  controller: ExampleComponent
+}
+```
+
+#### XosNavigationService
+Used to create custom navigation links in the left navigation panel.
+
+#### XosSidePanel
+Makes the injection of a custom side panel somewhat easier (no need to specify a target)
+
diff --git a/docs/developer/quickstart.md b/docs/developer/quickstart.md
index c0b05a0..a27c800 100644
--- a/docs/developer/quickstart.md
+++ b/docs/developer/quickstart.md
@@ -1,41 +1,42 @@
 # GUI Quickstart
 
-_We assume that you already have the code downloaded with `repo`_
+_We assume that you already have the CORD source downloaded with `repo`_
 
-## Setting up the `frontend` configuration
+## Setting up a podconfig
 
-The easiest to work on the XOS GUI is to create a local deployment of the `frontend` configuration of CORD.
+For front-end development, using a podconfig with your choice of CORD flavors and  the `mock` or `local` scenario is sufficient.
+`mock` will create a vagrant VM running all the necessary containers, while `local` directly creates the containers on your machine, 
+so we recommend using `mock` when developing on your own laptop/desktop.
 
-You can find detailed instructions in the `platform-install` documentation, but for all that we need you can: 
+Assuming you have already downloaded and ran the `cord-bootstrap.sh` script, setup is easy:
+
+```bash
+cd ~/cord/build/
+make PODCONFIG={CORD_FLAVOR}-mock.yml config
+make build |& tee ~/build.out
+```  
+
+Further details on the mock configuration and its setup can be found at [Mock Configuration Workflow](/xos/dev/workflow_mock_single.md)
+
+### Login credentials
+After the mock profile finishes building, you can find the credentials necessary to login into XOS by running the following:
 
 ```
-cd opencord/build/platform-install
-vagrant up head-node
-vagrant ssh
-// you're now inside the vagrant VM
-cd cord/buid/platform-install
-ansible-playbook -i inventory/frontend deploy-xos-playbook.yml
-```
-
-
-> This commands will spin up a local vm and deploy the minimal configuration of CORD in it. <br/>
-> _NOTE the first you'll execute this commands they may take a long time (~20 min)_
-
-#### Retrieve the password to access XOS
-```
-cat ~/cord/buid/platform-install/credentials/xosadmin@opencord.org
+cat ~/cord/build/platform-install/credentials/xosadmin@opencord.org
 // save the output somewhere
 ```
 
 ## Serving the GUI in development mode
 
-Once your basic CORD config is up and running you should be able to access the GUI at `http://192.168.46.100/xos/`
-but that's not your development copy, it is the one deployed inside a container in XOS.
+Once your basic CORD config is up and running you should be able to access the GUI at `http://192.168.46.100/xos/`.
+
+NOTE: This is not your development copy, it is the one deployed inside a container in XOS and will not change until
+the container is torn down and redeployed.
 
 To launch a development copy:
 ```
 // back to your local system
-cd opencord/orchestration/xos-gui
+cd cord/orchestration/xos-gui
 npm install
 npm start
 ```
@@ -45,9 +46,9 @@
 **Now you're ready to start working on it!**
 
 To get start, login using username `xosadmin@opencord.org` and the password you previously saved,
-then pick any file and make a change, you'll see the GUI reload.
+then pick any file and make a change, and you'll see the GUI reload.
 
-### Configuring the `dev` GUI
+## Configuring the `dev` GUI
 
 There are two configuration file available in the application, and they depend on the environment. You can find the various possibilities in `conf/app`, and they regard application constants, such as `apiEndpoint`, or branding elements, such as `projectName`.
 
@@ -55,6 +56,8 @@
 - `PROXY`: you can use this variable to send request to an arbitrary XOS installation (eg: `clnode022.clemson.cloudlab.us:8080`)
 - `BRAND`: to configure style constants (eg: `cord`, `opencloud`)
 
+## Working with an existing XOS installation
+
 You can also specify a different installation of XOS as backend by using the `PROXY` environment variable.
-For example you can connect to a remote CORD-in-a-box installation for debugging purposes with:
+For example, you can connect to a remote CORD-in-a-box installation for debugging purposes with:
 `PROXY=ms1106.utah.cloudlab.us:8080 npm start`
diff --git a/src/app/core/services/navigation.spec.ts b/src/app/core/services/navigation.spec.ts
index bc27772..b9cb84c 100644
--- a/src/app/core/services/navigation.spec.ts
+++ b/src/app/core/services/navigation.spec.ts
@@ -116,7 +116,7 @@
     service.add(testRoute);
     service.add(testRoute);
     expect($log.warn).toHaveBeenCalled();
-    expect($log.warn).toHaveBeenCalledWith(`[XosNavigation] Route with label: ${testRoute.label}, state: ${testRoute.state} and parent: ${testRoute.parent} already exist`);
+    expect($log.warn).toHaveBeenCalledWith(`[XosNavigation] Route with label: ${testRoute.label}, state: ${testRoute.state} and parent: ${testRoute.parent} already exists`);
     expect(service.query()).toEqual(defaultRoutes.concat([testRoute]));
   });
 });
diff --git a/src/app/core/services/navigation.ts b/src/app/core/services/navigation.ts
index e7fa4ed..660c144 100644
--- a/src/app/core/services/navigation.ts
+++ b/src/app/core/services/navigation.ts
@@ -71,24 +71,27 @@
     return this.routes;
   }
 
-  add(route: IXosNavigationRoute) {
+  add(route: IXosNavigationRoute, override: boolean = false) {
     if (angular.isDefined(route.state) && angular.isDefined(route.url)) {
       throw new Error('[XosNavigation] You can\'t provide both state and url');
     }
 
     // NOTE factor this out in a separate method an eventually use recursion since we can nest more routes
+    let preExisting = null;
     const routeExist = _.findIndex(this.routes, (r: IXosNavigationRoute) => {
-      if (r.label === route.label && r.state === route.state && r.parent === route.parent) {
+      if (r.label === route.label && (r.state === route.state || override) && r.parent === route.parent) {
+        preExisting = r;
         return true;
       }
       else if (_.findIndex(r.children, route) > -1) {
+        preExisting = r;
         return true;
       }
       return false;
     }) > -1;
 
-    if (routeExist) {
-      this.$log.warn(`[XosNavigation] Route with label: ${route.label}, state: ${route.state} and parent: ${route.parent} already exist`);
+    if (routeExist && !override) {
+      this.$log.warn(`[XosNavigation] Route with label: ${route.label}, state: ${route.state} and parent: ${route.parent} already exists`);
       return;
     }
 
@@ -97,6 +100,9 @@
       const parentRoute = _.find(this.routes, {state: route.parent});
       if (angular.isDefined(parentRoute)) {
         if (angular.isArray(parentRoute.children)) {
+          if (override) {
+            _.remove(parentRoute.children, r => r === preExisting);
+          }
           parentRoute.children.push(route);
         }
         else {
@@ -104,11 +110,14 @@
         }
       }
       else {
-        this.$log.warn(`[XosNavigation] Parent State (${route.parent}) for state: ${route.state} does not exists`);
+        this.$log.warn(`[XosNavigation] Parent State (${route.parent}) for state: ${route.state} does not exist`);
         return;
       }
     }
     else {
+      if (override) {
+        _.remove(this.routes, r => r === preExisting);
+      }
       this.routes.push(route);
     }
   }