VMware Hands-on Labs - HOL-1911-07-SDC

Lab Overview - HOL-1911-07-SDC - Building a vSphere Client Plugin Using the SDK

Lab Guidance

Note: It will take more than 90 minutes to complete this lab. If you need to stop in the middle you will be able to restart from the last section you completed (steps are provided in the next chapter). You can use the Table of Contents to access any module of your choosing.

The Table of Contents can be accessed in the upper right-hand corner of the Lab Manual.

The overview for this lab can be found in the Introduction of Module 1.

Lab Module List:

This lab manual can be downloaded from the Hands-on Labs Document site found here:


This lab may be available in other languages.  To set your language preference and have a localized manual deployed with your lab, you may utilize this document to help guide you through the process:



Location of the Main Console


  1. The area in the RED box contains the Main Console.  The Lab Manual is on the tab to the Right of the Main Console.
  2. A particular lab may have additional consoles found on separate tabs in the upper left. You will be directed to open another specific console if needed.
  3. Your lab starts with 90 minutes on the timer.  The lab can not be saved.  All your work must be done during the lab session.  But you can click the EXTEND to increase your time.  If you are at a VMware event, you can extend your lab time twice, for up to 30 minutes.  Each click gives you an additional 15 minutes.  Outside of VMware events, you can extend your lab time up to 9 hours and 30 minutes. Each click gives you an additional hour.



Methods of Keyboard Data Entry

During this lab, you will be asked to enter commands and code into the Main Console. Besides directly typing in it you can also select text from the manual and drag it to the console. However this doesn't work well for large text content. Instead we have provided a separate text file named Code snippets to copy.txt which contains the same text content and that you should keep open in Notepad or Notepad++. For details see "Important note on copying text from the Manual" at end of Module 1's Introduction, following this Lab Guidance.



Look at the lower right portion of the screen


Please check to see that your lab is finished all the startup routines and is ready for you to start. If you see anything other than "Ready", please wait a few minutes.  If after 5 minutes you lab has not changed to "Ready", please ask for assistance.


Steps to Restart from a Specific Section

This section describes the steps to help you restart the lab from a particular section.  Skip this if you are starting at the beginning of the lab!

We still assume that you have completed the previous sections in order to understand the concepts described further.


Get the code of myplugin corresponding to your starting section


  1. In Files Explorer select myplugin-solutions under C:\labfiles\HOL-1911\vSphereClientPlugin
  2. Drag the two folders myplugin-service and myplugin-ui-vN inside vSphereClientPlugin\myplugin
  3. Rename myplugin-ui-vN to myplugin-ui inside the folder myplugin (*)
  4. From vSphereClientPlugin drag node_modules to myplugin\myplugin-ui

(*) The version of myplugin-ui to use depends on where you are starting from:



Install node_modules

The plugin UI is a web application which depends on several Node modules both for its production build and its development setup. Since this lab doesn't have internet access we cannot use the npm command to download those modules. Instead we re-use an existing node_modules folder containing all the files needed.


In File Explorer select folder node_modules under vSphereClientPlugin and drag it into myplugin/myplugin-ui.   You should end up with the following:




Start myplugin in Dev Mode

Open a new Command Line window and go to myplugin-ui

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui


Start the json-server with this command:

 npm run json-server 



Open another Command Prompt you created and go to myplugin-ui:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui

Start the standalone version of myplugin UI with

npm start



Open myplugin UI in Chrome




Open Visual Studio Code



Double-click on the desktop icon on the left, or type Visual Studio in the Windows Start menu.

Open the folder myplugin to see the code if it is not already opened.

Open the text file Code snippets to copy.tx and keep it on the side of the screen. It will be easier to copy command and code snippets from that file than to drag text from the manual.




Deploy Plugin to vSphere Client

These final steps are only necessary if you are restarting from Module 2. Add View in Plugin Mode and Handle Errors.  They will be covered in Module 1.

Open a new Command Line and enter:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui 

This script takes about 30 seconds. You should see BUILD SUCCESSFUL once the script completes.

Start the vSphere Client's Virgo server with the following command in the same terminal window:


Starting the server may take a couple of minutes depending on the performance of the lab environment.

Open a new tab in Chrome and click on the Localhost bookmark or enter https://localhost:9443/ui

You will get a message that the vSphere Client web server is still initializing.  It is replaced by the Login screen after a while.

Click Use Windows session authentication to login.


Module 1 - Build a vSphere Client Plugin the Easy Way (45 minutes)


During this lab you will learn how to build and deploy a new vSphere Client plugin from scratch and take advantage of vSphere Client extensibility mechanisms. You will be using the vSphere Client SDK along with a leading-edge UI stack: Angular, Typescript, Clarity and Angular-CLI.

The content is fairly technical and it is recommended to have some web application development experience. But do not worry if you are not familiar with the UI stack or the vSphere Client SDK, the manual will guide you at every step!


vSphere Client SDK

vSphere Client SDK is a collection of libraries, tools, code samples and documentation that help you create vSphere Client HTML extensions to provide customized solutions in the vSphere UI.



You can browse the content of the vSphere Client SDK at the location shown above.  You can also browse the Developer Guide by opening the vsphere-client-sdk-67-programming-guide.pdf



Architecture Overview

The following diagram shows the 3 main layers of the vSphere Client architecture:

- The UI layer, in the user's browser, where the HTML/Javascript code runs.

- The Service layer, in the mid-tier application server, running the Java code. This layer handles the multiple browser sessions.

- The Back End layer consisting of vCenter servers and 3rd party servers.


In this lab we will focus on building a plugin UI, i.e. the top right maroon box.  The plugin UI takes advantage of Javascript APIs and extension points provided by the vSphere Client Platform (blue box).

A plugin usually contains Java service code running in the mid-tier layer.  This is a pass through to the back-end servers for requesting data or sending user actions, with very little business logic.  We don't have time to cover this service layer here but if you are curious you will be able to explore the Java code generated by the plugin seed tool.

NOTE: it is also possible to avoid using any java code in your plugin with the help of a proxy-servlet.  Please see the latest plugin-seed version available on the HTML Client Fling page at https://labs.vmware.com/flings/vsphere-html5-web-client



vSphere Client Extension Points

Extension points are the various places where a plugin can insert its own UI elements inside the vSphere Client in order to provide additional functionality.  The next screenshots describe the main extension points.


The shortcuts page and left navigator allow to add links as single entry points to the plugin UI.


The picture above is an example of a plugin global view, i.e. a view taking the whole workspace area, not related to any inventory object context on the left.

Global views are used by plugins to display their standalone UI, custom inventories, plugin-wide settings or server administration.


The Monitor and Configure tabs of vCenter inventory objects can be extended to include plugin views.


Plugins can also insert a "portlet" in the Summary view of vCenter inventory objects, i.e. a small area containing key plugin information.


Plugins can extend menus with their own sub-menus. Such menu items bring up a modal dialog or wizard to collect the user input before calling the backend, or they call directly their Java service entry point running in the mid-tier as shown in the architecture diagram above.



Plugin Compatibility and Sandboxing

This lab uses vSphere Client SDK 6.7 which lets you build HTML plugins compatible with vSphere Client 6.5u2, 6.7 and above.

If you need compatibility with the older vSphere Web Client (Flash) we recommend to use SDK 6.5.1 where you can build plugins which can run in both types of clients.

A key aspect of view extensions is the fact that they are “sandboxed” inside browser iframes. This sandboxing ensures that plugin views are isolated from the rest of the client and from other plugins. Each plugin can use the UI technology of its choice without depending on vSphere Client’s UI tech stack.  A recommended UI stack is the one used in this lab (Angular, Typescript, Clarity and Angular-CLI) and described further below.



Plugin Seed


Plugin seed is a tool which was created in addition of the vSphere Client SDK with three main goals in mind:

- Make it easier to start a vSphere Client plugin from scratch.

- Increase developer productivity with a fast development process and robust patterns.

- Use a modern UI stack.

This lab was built with plugin-seed 1.0.0 released in June 2018.  After you complete the lab you can download the latest version on the HTML Client Fling page at https://labs.vmware.com/flings/vsphere-html5-web-client  (look for plugin-seed-<version>.zip in the drop down box)



UI Tech Stack

NOTE: In addition to the four main technologies listed below plugin seed uses several tools and libraries that you can explore later in the lab.  All UI dependencies are listed in the file package.json of the plugin project.


Google's Angular framework is the leading Javascript framework for building entreprise web applications.  Version 6 is used here.

To learn more on Angular, outside this lab environment, go to https://angular.io


The Typescript language has become an industry standard for web applications as well.  It is 100% compatible with Javascript, very easy to pick up and well integrated with Angular.

To learn more on Typescript, outside this lab environment, go to http://typescriptlang.org



Clarity is a VMware open-source project which provides a rich set of UI components.  Another advantage of using Clarity is the UI consistency with the rest of the vSphere Client UI which is also using Clarity components.

To learn more on Clarity, outside this lab environment, go to https://vmware.github.io/clarity/documentation



Angular-CLI provides plugin seed with the Angular project skeleton, a build system and many other facilities such as test configurations. It will be used in this lab to generate new Angular components and services and add them to the existing plugin code.

To learn more on Angular-CLI, outside this lab environment, go to https://github.com/angular/angular-cli



Dev Mode vs. Plugin Mode

The plugin UI is built initially in Dev mode, i.e outside the vSphere Client, as an integrated single page web application. In this mode the plugin is a simple web application with several views sharing a common a header and navigator, i.e. the extra "dev UI". It is isolated from backend services by using mock data. The development cycle is faster because you can focus exclusively on building the UI. vSphere Client doesn't need to be the picture.

When the same code is deployed with vSphere Client the Plugin mode is turned on automatically, which means that each extension point knows to display the correct view. Data is served from production services, i.e. rest calls to backend services. Any extra dev UI is hidden automatically.

By having a clear separation between Dev and Plugin modes it is also easier to run unit tests and end-to-end tests outside the vSphere Client.





Important Note on Copying Text from the Manual

This lab contains a fair amount of commands to enter, and HTML / Javascript code to type. Unfortunately there is no fast way to copy/paste text from the manual to the lab console.   Single line commands can be copied using the drag & drop tool as shown in the picture below.  For bigger text sections we have provided a file named Code snippets to copy.txt that you should keep open in Notepad or Notepad++ so that you can  copy the same text referenced in the manual.

Drag & drop of single-line commands

Drag the selected text to the Command Prompt window as shown below.
NOTE: the text is sent at a slow pace!  DO NOT click anywhere else in the console while this is happening.

Once the text is sent, click in the Command Prompt and hit Enter to execute the command.


All text input is in file: Code snippets to copy.txt



Getting Started with the Plugin Seed


Prepare to generate and run your first plugin

All the work for this lab will take place in the following directory:  C:\labfiles\HOL-1911\vSphereClientPlugin


Open the text file Code snippets to copy.txt and keep it on the side of the screen. It will be easier to copy command and code snippets from that file than to drag text from the manual.


Open a Command Prompt window.



Go to the plugin-seed directory:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\plugin-seed

Launch this script to generate your first plugin:




The script brings up three prompts:

  1. Hit Enter to take the default name myplugin
  2. Type ..\myplugin as the parent directory in the 2nd question
  3. Hit Enter in the 3rd question to take the default package name com.mycompany.myplugin

You should see BUILD SUCCESSFUL in the console once the code is generated.


Open Files Explorer and select the folder myplugin under vSphereClientPlugin.  The new plugin code is divided into myplugin-service for the Java code and myplugin-ui for the front-end. We will work on the front end in directory myplugin-ui.

NOTE: if you were not able to generate this new plugin you can get version v0 located in myplugin-solution:  drag folders myplugin-service and myplugin-ui-v0 into myplugin, then rename myplugin-ui-v0 to myplugin-ui.



Open Visual Studio Code


In this lab we will use Visual Studio Code as our development environment for editing HTML and Javascript code.

Double-click on the desktop icon on the left, or type Visual Studio in the Windows Start menu.


Open the File menu and verify that the Auto Save option is turned on.

Every code change will be applied directly to the running plugin. It is better not to have to remember to save your changes!



See Code Folders myplugin-service and myplugin-ui


Open the folder myplugin to see the plugin code (if it is not already opened) and close any file that may have been left opened.  You should see the left Explorer as above.

As mentioned earlier the generated code in split into two projects. In this lab we will leave plugin-service, the generated java code, as-is.  

Expand myplugin-ui to explore the UI code.  

If you are familiar with Angular-CLI you will recognize the files organization of an Angular-CLI application and its configuration files.  If you are not, don't worry, we will explain all changes in details!



Install node modules

The plugin UI is a web application which depends on several Node modules both for its production build and its development setup. Since this lab doesn't have internet access we cannot use the npm command to download those modules. Instead we re-use an existing node_modules folder containing all the files needed.


In File Explorer select folder node_modules under vSphereClientPlugin and drag it into myplugin/myplugin-ui.  

NOTE: If you are curious about these Node modules dependencies see the file myplugin-ui/src/package.json for the full list.



Start json-server to serve mock data

A common practice is to separate the UI from backend services with the help of mock data. This speeds up the development process.  Our plugin uses the json-server tool as a simple backend for prototyping and mocking. Other mocking solutions are available but json-server is very simple to set up and supports REST APIs.


Open a new Command Prompt window and go to myplugin-ui

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui



Start the json-server with this command:

 npm run json-server 

The server responds with a list of its API resources and its home URL http://localhost:3000

NOTE: Do not close this new Command Prompt window for the rest of the lab because json-server needs to be running!



Start myplugin in Dev mode

As mentioned in the introduction the best way to start working on a new plugin is in Dev mode, i.e. the plugin UI is developped initially as a pure Angular application, outside the vSphere Client itself.


Switch back to the first command prompt you created (where the current directory should be plugin-seed) and enter:

cd ..\myplugin\myplugin-ui
npm start

npm start builds a runtime version of the plugin app in a few seconds. It launches a development server which makes the app available available at port 4201.

The final output should look like the screenshot above. This is standard for an Angular-CLI application, the build creates several .bundle.js files which contain the application components.

NOTE: if npm start gives you an error like "Error: ENOENT: no such file or directory" it must be because the node_modules folder is missing under myplugin-ui. Go back and make sure you drag it into the right location.



Explore myplugin UI


Open Google Chrome from the desktop, or from the Start menu.


Chrome starts initially with a tab opened with vSphere Web Client, which won't be used here.

Replace that URL with http://localhost:4201/, or select the bookmark myplugin at the very end of the bookmarks bar.

This opens up myplugin's Home page.  The generated UI comes with various tools that we will explore next.



myplugin Dev UI


The top blue header and left inventory are UI elements added by default to all new plugin samples.  They facilitate navigation in Dev mode.  The red rectangle shows the area that will be left visible once myplugin is running with vSphere Client.

A simple way to isolate the real plugin content from Dev UI components is to click on the (X) in the top left corner


Click on the (+) to restore the full application in Dev mode.

You will see how this Dev UI logic is implemented once we explore the HTML code a little later.



Echo Service


The Home page shows two tabs as examples.  Click on tab Echo Service.

This view demonstrates how to call a Java service running on the backend and displays the result in a popup window. Two popup implementations are provided, depending on whether you need to popup to be modal for the plugin view only or for the whole vSphere Client application.

The left navigator displays a list of "mock" hosts.

Click on mock-Host 5



Host object views


The seed plugin replicates the standard object views of vSphere client, with Summary, Monitor, and Configure tabs. It extends Host objects by default but it would be easy to change to another type of vSphere object.  The goal is to provide a starting point for plugin developers who want to add features to particular object types.

The mock data seen here is served by the json-server started earlier. The data is defined in a local file db.json that we will review next.

NOTE: Action1, Action2 and Action3 buttons are examples of user action implementations, either through a modal dialog or with a direct http call. They are out of scope for this lab, please refer to the plugin seed and SDK documentations for more details.



Backend APIs mocked with json-server


Open another Chrome tab and enter


This URL is a simple API which returns the list of mock hosts in json format as defined in db.json.  URL http://localhost:3000 is the default address for json-server.

To access a single host add the id at the end of the URL, like this:




Add a mock Host 6


Go back to Visual Studio Code.

Expand myplugin-ui

Select the file db.json at the top level under myplugin-ui.  This is the same json data displayed in the browser.

Scroll down to the hosts section and copy the block with id: 4 and paste it at the bottom of the file, then change all 4s with 6s, like this:

      "id": 6,
      "name": "mock-Host 6",
      "status": "red",
      "model": "VMware Virtual Platform",
      "vms": [

WARNING: Each block and element must be separated by a comma, except at the end. This is the required json syntax.

NOTE: You can use this Visual Studio Code shortcut to replace all "4"s with "6"s:

  1. Select the first 4.
  2. Hit CTRL-D several times to add the next 4s.
  3. Enter 6 to replace all 4s at once.

Your changes are saved automatically.

Go back to Chrome.


Click the Refresh button in the header, next to Home.

  1. Select mock-Host 6.  The updated mock data is available.
  2. The browser URL reflects the new selected host.


NOTE: if you do not see any data at this point it must because there is an error in the file db.json you just modified.  



Small improvement to the Host Monitor view

Now that we have done a quick tour of the generated plugin let's add more functionality to the host Monitor view. The current implementation only shows the number of VMs per host. We would like to make the Virtual Machines line expand with the list of VMs.

Switch back to Visual Studio Code



Host Monitor HTML template


Expand the source tree: myplugin-ui > src > app > views > monitor and select monitor.component.html

This is the HTML template for myplugin's Host Monitor view. The top of the file contain the Dev mode components we talked about earlier.

  1. The header is toggled on/off with the line <app-header *ngIf="gs.showDevUI()" ></app-header>
  2. *ngIf is an Angular directive taking the result of a globalService function showDevUI()
    (this Dev UI state changes when you click on the top left X as we saw earlier)
  3. Likewise the side tree navigator is toggled on/off with <td *ngIf="gs.showSidenav()" class="td-sidenav">
  4. The real view content is inside the  <div class="content-container> element.


Here are the elements that make up the Monitor view:

  1. {{titleKey | translate}} {{host.name)}} displays the view title which contains the host name inside the string resource. It uses the ngx-translate library to convert the string to other locales (we will switch to French in the next step)
  2. <clr-stack-view is the Clarity stack view component which stacks up the host information.
  3. The top <clr-stack-block> uses {{host.name}} to display the host name.  This is the way Angular variables are displayed inside HTML templates.
  4. The 2nd stack block includes an icon whose value is computed in monitor.component.ts (info, warning or error)
  5. The next stack block displays the value of host.model
  6. The last stack block displays the number of VMs: {{host.vms.length}}, i.e. the size of the host.vms array.
  7. Two buttons follow the stack view. The buttons' click handler is implemented with the (click) event.

Switch to Chrome.



Switch between English and French


Click on the French flag in the top right corner.

The text inside the Monitor view content changes to French.  i.e. all string resources using the syntax {{key | translate}} in monitor.component.html

The surrounding dev UI elements remains in English on purpose: it doesn't need to be translated because it won't be visible when the plugin is installed with vSphere Client.

Switch back to Visual Studio



Make the "Virtual Machines" block expandable


We are going to use the Clarity stack block attribute clrSbExpandable to make the Virtual Machines element expandable.

Find the last <clr-stack-block in the HTML template at line 42 and change that line to:

NOTE: Remember that you can copy this text from the file Code snippets to copy to be more efficient.

The boolean expression host.vms.length > 0 means that the block will be expandable only if there is at least one VM to display.

The change is auto-saved and recompiled automatically as you can see in the ng Command Prompt window. Switch back to Chrome to see a caret next to "Virtual Machines" for all hosts with one or more VMs.


Nothing interesting happens yet when clicking on Virtual Machines. The caret moves down and up.  Let's add more code.

Go back to Visual Studio Code



Angular *ngFor Loop


In order to add a sub-list of stack block elements containing the VMs we use Angular *ngFor directive which repeats HTML code in a loop.

Add this code below line 44 as shown above:


This generates an array of stack blocks, with one element per vm in the host.vms list. Each one display the vm value with the expression {{vm}}

Switch to Chrome

The code is recompiled quickly and myplugin tab is auto-refreshed.  Now, when expanding Virtual Machines, you see the list of VMs for each host. This is the list defined in the mock data file.




Check other hosts


Virtual Machines is expandable for all other hosts, except for mock-Host 5 which doesn't have any VM.

Now that we have made these small changes to myplugin in Dev mode, let's deploy it to the vSphere Client and see the same UI in Plugin mode!

We are done with our first look at the generated plugin. Go to the next chapter Deploy myplugin to vSphere Client.


Deploy myPlugin to vSphere Client

In this section we will install the newly created plugin with the local vSphere Client and show how you can develop a plugin both in Dev Mode and Plugin Mode.

NOTE: if you are restarting the lab directly from this section please follow Steps to Restart from a Specific Section under the Lab Overview. It contains the steps required to resume the lab quickly from various points.


Build the Plugin Package


The three steps required to deploy the plugin with vSphere Client are 1) build its UI and service bundles, 2) package them into a plugin package folder, and 3) add this folder to the local vSphere Client setup for testing.  This is done with the script build-plugin-package


Open a new Command Prompt  


Enter the following in the command line window:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui 


This script takes about 30 seconds. It compiles both the UI and Java bundles of myplugin, generates a plugin package folder and copy it at the right location for the local Client.

You should see BUILD SUCCESSFUL once the script completes.




Start vSphere Client

A vSphere Client instance is included in the vSphere Client SDK installed on this Windows machine, its runtime is called the Virgo server.  Once the script build-plugin-package.bat completes it gives this message as a reminder that a new plugin package requires a server restart.

 [echo] *** You must restart the Virgo server to use the new plugin package! ***

Start the Virgo server with the following command in the same terminal window:


Starting the server may take a couple of minutes depending on the performance of the lab environment. (Luckily, during normal development, it doesn't need to be restarted very often because UI changes can be made live as we will see later.)


Open a new tab in Chrome and  click on the Localhost bookmark or enter https://localhost:9443/ui which is the entry point for the local vSphere Client.

You will get a message that the vSphere Client web server is still initializing.  It is replaced by the Login screen after a while.



myplugin in plugin mode


Click on Menu

Click on Shortcuts

You may need to either expand the browser size or minimize the Recent Tasks view at the bottom in order to see enough content.


Plugins can add an icon in the Shortcuts page to access to their main view. Click on myplugin, it opens the same view you can see running in Dev mode in the other tab, without the blue header and sidenav elements that are used for development purposes.



myplugin - Main View


The myplugin Main View is using a "global view" extension provided by the Client SDK to insert a general view which is not related to a particular vSphere object. The generated plugin also provide a Settings view that we will use later to get some client ID information.

The Chassis App view is an example of UI for displaying custom objects but it won't be covered in this lab.



Select Hosts and Clusters in the Home menu.



myplugin - Host View


Expand the vSphere hierarchy on the left until you get to a host, i.e. esx-02a.corp.local.

Click on the Monitor Tab

Click on myplugin

Expand the Virtual Machines section

The plugin view displays real Host and VM data, instead of the mock data used in Dev mode. It is sending http requests to the plugin Java services running in Virgo and accessing the vCenter backend.

Now that our plugin is running in the local vSphere Client we are going to use the plugin's Java services to replace mock data with “live data” in the standalone app!



Live data in Dev mode

Mock data is useful to start prototyping but at some point you also want to test with real data coming from backend services.  The “best of both worlds” would be to connect to real vSphere data while still in Dev mode.  Plugin-seed provides a simple mechanism to achieve that!


Go back to the tab where myplugin runs as standalone UI at URL http://localhost:4201/main

Click on the Live data switch in the header.

This has the effect to change the data request URL from the mock json-server endpoint to the java service endpoint, running in the Virgo server. You see an http error because the request going to Virgo is not authenticated.  Java services running in Virgo can only access vSphere data through an authenticated session obtained through the vSphere Client login.

To work around this error we are going to re-use a valid user session id, i.e. Client id, from vSphere Client running in the same browser!

Turn off Live data for now.

Go back to the vSphere Client tab.

The generated plugin has a built-in tool for setting the Client id, it is located in the Settings view in the Administration area.




myplugin Settings View


Click on Menu

Click on myplugin

This brings you back to myplugin's main interface.


Click on Settings View

A typical plugin would display global settings elements in such a view.




Copy Client Id from vSphere Client to myplugin


Copy the Client id value with Ctrl-C, or with the right-click menu Copy item.

Click Close


Go back to myplugin UI

  1. Click on the Settings icon in the header.  This is the same Settings page, except that the GET CLIENT ID button now says SET CLIENT ID.
  2. Click SET CLIENT ID


Click in the text field and enter Ctrl-V to paste the Client Id value that you copied earlier.

Click Save



Live Data Switch Turned On or Off


Once a valid Client Id has been entered in the Settings view you can turn the Live data mode on and off without errors.

When Live data is on the plugin displays real hosts data, i.e. those are the same Hosts from the vCenter inventory visible in vSphere Client.

NOTE: If the Live data mode was already on when you added a Client Id in Settings you may need to hit the Refresh button.


Click the Live data switch off and see the UI switch back to mock hosts data.

You can go back and forth between live data and mock data at anytime now that the Client Id is set up correctly!  This is very convenient for testing java services while still working in dev mode.

Live data stops working only when the user session times out, i.e. if you leave the UI idle for more than two hours.  After that you can go through the same steps and copy another Client Id from the Settings page in vSphere Client.



Fast UI Updates in Plugin Mode

We have seen in the previous chapter that code changes are reflected right away in dev mode, but it is also possible to quickly refresh the plugin UI code loaded in the vSphere Client.

This is done with a watch script which compiles and pushes those changes to the vSphere Client.


Open a new command prompt and do the following:

cd C:\LabFiles\HOL-1811\webClientPlugin\generated\myplugin-ui
npm run watch

The watch script compiles the UI code and uses Angular-CLI “watch mode” to react to code changes.  It takes a few seconds to initialize that mode, the terminal shows the UI build being updated once, with the javascript bundles in green.

Now that the watch mode is running future code changes will trigger a fast incremental build.  Let's try it, switch to Chrome.



Monitor View Navigation Buttons


The current Host > Monitor view has two  navigation buttons GO TO SUMMARY and GO TO CONFIGURE to jump to the other tabs.

We are going to add a third button and see the changes reflected in both the standalone app and in vSphere Client.



Add a New Button

In Visual Studio expand myplugin-ui > src > app > views > monitor and open monitor.component.html

Scroll down to line 50 where the two buttons are implemented <button...


Add this button element under the first two (you can copy the text from the file Code snippets to copy.txt):

This implements a third button which will navigate to the main view.


The ng terminal window where you started npm run watch shows the compilation between triggered by the code change



Fast UI Updates in Dev Mode and Plugin Mode


myplugin UI is refreshed automatically with this third button.


The refresh of myplugin UI inside vSphere Client is not automatic because in this case myplugin is not the main application.

  1. Go back to the vSphere Client tab.
  2. Switch from one host to another to force a refresh of myplugin Monitor view
  3. See the new button visible there as well.

You can verify that this new button works too!



Updating HostService to Display VM Names

The change made earlier to the Monitor view shows Virtual Machine IDs, not their names.  Let’s make another change to get the real VM names from the backend.

NOTE: As you make more code changes throughout this lab the Chrome tab myplugin will be refreshed automatically.  You may get a black screen showing Failed to Compile and a compilation error message.  This is normal if it takes several steps to complete a correct implementation.



Add a Response to Expansion Action


Go back to  monitor.component.html in Visual Studio Code.

Scroll up to line 42.

The Clarity stack block component supports an event handler clrSbExpandedChange to notify you of the stack expansion.  Let’s add a function call toggleVmBlock() in response to that notification:

Remove the ending > on line 42 and add an extra line like this:


NOTE: the syntax (clrSbExpandedChange) is for event binding, whereas the syntax [clrSbExpandable] is for property binding.



Implement toggleVmBlock()


Highlight the monitor.component.ts file and scroll to the bottom

Just above the last } enter the following code:

    vmBlockExpanded = false;

    toggleVmBlock(host: Host): void {
       this.vmBlockExpanded = !this.vmBlockExpanded;
       if (this.vmBlockExpanded) {
           console.log("will call HostService to get VM names");

This initial implementation toggles a vmBlockExpanded state and displays a console message.


At line 72 you also need to make this small change to make sure the VM data is refreshed each time the user selects another host:

    } else if (this.vmBlockExpanded) {


Let's open the Chrome browser console to see the code in action.



Browser Dev Tools Console


Switch to Chrome

Open the Chrome Developer Tools with  Ctrl-Shift-I or right-click and select Inspect.

Click on the Console tab.


Click and Expand the Virtual Machines list

See the message displayed in the Console tab which confirms that the console.log code you just added is working.

Close the Dev Tools panel.

The next step is to add a function getHostVMs(hostId) in HostService to retrieve the actual VM names.  The returned value should be an array of VM objects which include both an id and a name since the id is the only way to identify each VM uniquely.

Switch to Visual Studio Code



Implement fetchVMs()


1. In monitor.component.ts replace line 132 with this.fetchVMs(); which will perform the logic of retrieving VM names.

2. Scroll down to bottom of the file, just before the last } enter the following code to implement fetchVMs:

   fetchVMs(host: Host): void {
            vms => host.vms = vms,
            errorMsg => this.appAlertService.showError(errorMsg)

3. We're calling a new hostService function getHostVMs() which will return an array of VM objects containing their name and id. (The red underline indicates a Typescript compile error because this function is not implemented yet)

NOTE: getHostVMs  is an asynchronous call, returning an Observable (i.e. a data stream). In order to get the data we must subscribe to that Observable. Then we can assign the returned array in case of success and display the error message in case of failure.




Add a VM Model


We need a new “VM model” class to represent the VM data fetched by our new function. Let's add a new file vm.model.ts in /src/app/services:

Right click on app/services and click New File.


Name the new file vm.model.ts and copy the following code:

 * A simple VM object model
export class VM {
   id: string;
   name: string;

   constructor(name: string = null, moRef: any = null) {
     this.name = name;

     // For real VMs the id is built from moRef attributes (ManagedObjectReference),
     // such as urn:vmomi:VirtualMachine:vm-192:8a785c39-0739-459d-bc8b-dffaf451ba25.
     // For mock VMs id and name are the same, like "vm-10".
     this.id =  moRef ?
           ("urn:vmomi:" + moRef.type + ":" + moRef.value + ":" + moRef.serverGuid) :

Our model doesn't need more than the id and name fields.  For a more complex application you could generate Javascript models directly from Java DTOs (data transfer objects).

The class constructor is setup to handle the case of real VMs:  the moRef object is what will be returned by the Java service.  The VM id must be built following the precise syntax described in the comment.



Update HostService


Open host.services.ts and scroll to the bottom of the file.

Just above the last }  paste the following code that implements getHostVMs()

   getHostVMs(objectId: string): Observable {
      const options = this.gs.getHttpHeaders();
      const useLiveData = this.gs.useLiveData();
      const url =  useLiveData ? (this.gs.getWebContextPath() + "/rest/data/propertiesByRelation/"
          + objectId + "?relation=vm&targetType=VirtualMachine&properties=name")
          : "http://localhost:3000/hosts/" + objectId;
      return this.http.get(url, options).pipe(
         map((response: any) => {
            return useLiveData ?
               response.map(vmResult => new VM(vmResult.value, vmResult.resourceObject)) :
               response['vms'].map(vmId => new VM(vmId));

There is still a compilation error left at line 130 and 131 because the symbol VM cannot be found. We will fix it in a moment.



getHostVMs Technical Details

NOTE: This  section explains getHostVMs() in depth and can be skipped for time sake. In that case continue at the next screen Missing Import error.

Let's look at the code for getHostVMs().  In Live data mode the value of const url looks like this:



It is useful to try this query manually in your browser to understand the returned result.  For that purpose you need to copy the object id of a Host in your system:

  1. Go back to the Chrome tab with vSphere Client
  2. Select a Host in the inventory
  3. Look at the browser URL, scroll horizontally to the place where it contains the host objectId, something like this:
  4. Copy the objectId value between = and &
  5. Paste that host id in the URL given above, in place of <host-id>: https://localhost:9443/ui/myplugin/rest/data/propertiesByRelation/<host-id>?relation=vm&targetType=VirtualMachine&properties=name
  6. Copy that new URL into another browser tab and see the result, it should look like this json data:
      resourceObject: {
         value: "vm-419",
         type: "VirtualMachine",
         serverGuid: "8d88c879-6e53-4d70-a00b-9a725d121077"
      currentGenerations: null,
      value: "some VM name",
      propertyName: "name"
      resourceObject: {
         value: "vm-420",
         type: "VirtualMachine",
         serverGuid: "8d88c879-6e53-4d70-a00b-9a725d121077"
      currentGenerations: null,
      value: "other VM name",
      propertyName: "name"

Given this data it's easier to understand how the response of the http.get call is handled in the code below:

     return this.http.get(url, options).pipe(
         map((response: any) => {
            return useLiveData ?
               response.map(vmResult => new VM(vmResult.value, vmResult.resourceObject)) :
               response['vms'].map(vmId => new VM(vmId));

Either we're in Live data mode and we call the VM constructor with parameters vmResult.value, vmResult.resourceObject, or we're using mock data and we just need to pass the vmId.



Missing Import Error


On lines 130 and 131 VM has a red underline and shows a [ts] error when hovering with the mouse. The typescript compiler tells us that it doesn't know yet about the VM symbol.

Set the cursor on VM: a yellow light bulb appears on the left of the line.

Click on the light bulb to add the correct import VM statement and resolve the compiler error.

See this new line at the top of the file: import { VM } from "./vm.model";

After this fix the compilation error is also gone from Command Prompt window running the watch mode.



Final update to monitor.component.html


We are almost done with the code required to display the real VM names.  If you expand Virtual Machines in the plugin UI at the point you get [object Object] instead of a VM name. It's because the HTML template still contains the value {{vm}} but vm is no longer a string, it is a VM object.


Open monitor.component.html and change {{vm}} to {{vm.name}} on line 47.


Real VM names are now displayed in Live data mode.


The same change is visible in Plugin mode  (as long as you kept the watch process running in the command line window)



Summary and Next Steps

You have reached the end of this module.  Here is a summary of what was accomplished:

  1. Creation of a new plugin skeleton with the script generate-plugin
  2. Run the plugin UI as a standalone web app, i.e. in "Dev Mode"
  3. Extend the mock data in db.json, served by the json-server tool
  4. Explore the language support
  5. Change the Host Monitor view to make the VM block expandable
  6. Build the plugin package and deploy myplugin to the local vSphere Client provided in the SDK
  7. See myplugin running in vSphere Client, i.e. in "Plugin Mode"
  8. Use the Live data switch to get real data in Dev mode.
  9. Use the npm run watch script to refresh the UI quickly in Plugin Mode.
  10. Update the HostService to display VM names instead of ids
  11. Use the Browser console to debug and check for errors
  12. Review in details the getHostVMs function which deals with backend data
  13. See the VM names in the UI for both Dev mode and Plugin mode

NOTE: If you didn't finish these steps and would like to catch up you can copy myplugin-ui-v2 from myplugin-solutions into your folder myplugin. Before deleting your existing myplugin-ui please move its node_modules folder into myplugin-ui-v2 as it must be re-used! Then rename  myplugin-ui-v2 to myplugin-ui to proceed.

The next module will show how to work in Dev Mode to add a new VM Monitor view, how to handle errors and how to complete the plugin update to test it in vSphere Client.  You can resume that module at a later time by following the instructions in Steps to Restart from a Specific Section.


Module 2 - Extend the Plugin UI and Handle Errors (30 minutes)

Add a VM Monitor View

In this section we will add a brand new plugin view, under the VM > Monitor tab, to display some Virtual Machine CPU and Memory statistics. First we will add the view UI in Dev mode using Angular-CLI commands.

NOTE: if you are restarting the lab directly from this section please follow Steps to Restart from a Specific Section under the Lab Overview. It contains the steps required to resume the lab quickly from various points.

Scroll down the content of the text file Code snippets to copy.txt to Add a VM Monitor View so that you're ready to copy the code changes.


Add the View Component

First we create a vm-monitor directory under myplugin-ui/src/app/views



Open a new Command Prompt and enter the following:

cd C:\labfiles\HOL-1911\webClientPlugin\generated\myplugin-ui\src\app\views 
ng generate component vm-monitor --styleext=scss

We take advantage of Angular-CLI generate command to create a new UI component and all its associated files:

The --styleext option is used to replace the default .css with .scss (SASS)

See the 4 files generated in ./vm-monitor.  Note that VmMonitorComponent is also added automatically to the default module app.module.ts because it must be declared with other components.

In order to open this new component in the app we must define its URL path in the list of appRoutes, in app/app-routing.module.ts.

Switch to Visual Studio Code




Add a Route for the New Component


Expand myplugin-ui > src > app, and select the file app-routing.module.ts

Find the section at line 19 defining the array const appRoutes : Routes = [

Add this line as a new element under line 45.

{ path: "vm-monitor/:id",   component: VmMonitorComponent },

This tells the application to open VmMonitorComponent whenever the URL path is .../vm-monitor/some-vm-id.

VmMonitorComponent is underlined in red because the typescript compiler doesn't know about it yet. Click on it and a lightbulb appears on the left to solve this as we have seen before. Click the pop up that appears. This adds the import statement at the top of the file (or you can also add this line manually):

import { VmMonitorComponent } from "./views/vm-monitor/vm-monitor.component"; 




Testing our New Component Path


You can now try the path of the new component in the browser:

Open a new Chrome tab.

Enter http://localhost:4201/vm-monitor/1

This displays the view HTML content, which is the string "vm-monitor works!" as generated by Angular-CLI in vm-monitor.component.html



Declare the New /vm-monitor Route


We must also declare the new /vm-monitor route in the extensionToRoutes map because it will be used later by the navigation service to go from our plugin's Host view to its VM view.

Find the section export const extensionToRoutes = { }; below the last edit you made and paste this line somewhere in the block:

extensionToRoutes[APP_CONFIG.packageName + ".vm.monitorView" ] = "/vm-monitor";

NOTE: The application should still function normally but with no visible change at http://localhost:4201/  If you made a typo and there is a compile error it will be visible on the screen.



Add a VM Service to Retrieve Data

We need a new Angular service to retrieve the stats for a Virtual Machine, similar to the HostService in host.service.ts used to retrieve Host data.

We will generate VmService with Angular-CLI.


You can re-use the command line window where you created VmMonitorComponent.

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui\src\app\services
ng generate service vm

In Visual Studio Code, see the files vm.service.ts and vm.service.spec.ts (unit tests) generated in /services.


VmService must be added manually to app.module.ts, in the array of service providers as shown above.  (Resolve the error with the same Import shortcut as below.)



Expand the VM Model

The VM model class created earlier needs to be expanded to hold more VM data retrieved from the vCenter server backend.  We want to display the following VirtualMachine properties (those are defined in the vSphere API for VirtualMachine):



Open the file services/vm.model.ts in Visual Studio Code.

Set the cursor under the name field and copy these additional fields:

    numCPU: number;
    maxCpuUsage: number;
    overallCpuUsage: number;
    overallCpuDemand: number;  // "summary.quickStats.overallCpuDemand"
    maxMemoryUsage: number;    // "runtime.maxMemoryUsage"
    consumedOverheadMemory: number; // "summary.quickStats.consumedOverheadMemory"
    hostMemoryUsage: number;   //  "summary.quickStats.hostMemoryUsage"

The new field names are the short-name versions of the VM properties, i.e. the last part of the full name after "."



Add Mock VM Data

Now that we updated the VM model let's add some mock data in db.json to match this model.


Open db.json

Insert the cursor before "hosts: [ and add an empty line.

Select then copy this json data for three VMs:

"vms": [
   "id": "vm-10",
   "name": "mock-VM 1",
   "numCPU": 1,
   "maxCpuUsage": 2500,
   "overallCpuUsage": 2000,
   "overallCpuDemand": 1500,
   "maxMemoryUsage": 4096,
   "consumedOverheadMemory": 2000,
   "hostMemoryUsage": 3000
   "id": "vm-11",
   "name": "mock-VM 2",
   "numCPU": 2,
   "maxCpuUsage": 4000,
   "overallCpuUsage": 1500,
   "overallCpuDemand": 1000,
   "maxMemoryUsage": 2048,
   "consumedOverheadMemory": 1000,
   "hostMemoryUsage": 1500
   "id": "vm-12",
   "name": "mock-VM 3",
   "numCPU": 4,
   "maxCpuUsage": 3300,
   "overallCpuUsage": 1500,
   "overallCpuDemand": 0,
   "maxMemoryUsage": 2048,
   "consumedOverheadMemory": 20,
   "hostMemoryUsage": 26

Let's check this new vms API in the browser:


Open a new Chrome tab at http://localhost:3000/vms

Verify that the VM data you just added is served through the json-server.

To get data for a specific VM add it id to the path, like http://localhost:3000/vms/vm-10, http://localhost:3000/vms/vm-11 , etc.

WARNING: If no data is returned in the browser it must be because of an invalid character in db.json. Check the command line window where you started the json-server, it will report errors with a line number.



Live VM Data

Let's look at the "live data" URL to retrieve the same VM properties.


The live data URL for VMs has the same format as for Hosts seen in the previous chapter, but uses a VM id and the list of VM properties. The picture above shows how you can copy the VM id embedded in the browser URL.  Below is the live data URL for that particular VM:

Open a new Chrome tab and copy this entire URL:



The result of the http request is the json data returned by myplugin's service, with the values for that VM.

See that the property names are returned in their long format since it is what the request contained. We will need some code to convert them to their short format.

NOTE: If this URL doesn't return any data it may be because your current vSphere Client session has expired.  Go to https://localhost:9443/ui and login again

With data request in mind we can now write a getVmProperties() function which will use the Angular Http service and return an Observable (i.e. a stream) containing the VM data.

Switch to Visual Studio Code



Modify VmService


Open vm.service.ts under src\app\services

Replace the previously generated content with the following code:

import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { VM } from "./vm.model";
import { GlobalsService } from "../shared/globals.service";
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

export class VmService {
  constructor(private http: HttpClient,
              private gs: GlobalsService) {

The VmService constructor injects two existing services, this is the Angular way to make services available to classes.

- HttpClient is the Angular service which allows to call backend APIs through standard http calls.

- GlobalsService is a built-in utility provided by plugin-seed to access global state information.



Add Function getVmProperties()


Insert the following code just above the closing }

   getVmProperties(objectId: string, properties: string[]): Observable {
      const headers = this.gs.getHttpHeaders();
      const url = this.getUrl(objectId, properties);

      return this.http.get(url, headers).pipe(
            map((response => this.trimObjectKeys(response) as VM))

The implementation of getVmProperties means the following:



Add Function getUrl()


Insert this code for the getUrl() function:

  private getUrl(objectId: string, properties: string[]): string {
    let url: string;
    if (this.gs.useLiveData()) {
       const propStr = properties.toString();
       url = this.gs.getWebContextPath() +
             "/rest/data/properties/" + objectId + "?properties=" + propStr;
    } else {
       url = APP_CONFIG.getMockDataUrl(this.gs.isPluginMode()) + "/vms/" + objectId;
    return url;

You also need to import APP_CONFIG to solve the compile error.

The url variable constructed in getUrl uses the format we tested earlier in Chrome.  FYI, gs.getWebContextPath() returns /ui/myplugin (the plugin web context).  So the whole URL looks like this:




Add Function trimObjectKeys()


The expression response => this.trimObjectKeys(response) as VM is for converting the http response body into a VM object.  The trimObjectKeys function is necessary to replace the long format of the property fields from the http response into the short format used in our VM model, for instance to convert runtime.maxCpuUsage into maxCpuUsage.

Select then copy the following code for the last two function utilities:

   // Transform an object by trimming its key names
   trimObjectKeys(obj: any): any {
      return Object.assign({}, ...Object.keys(obj)
         .map(key => ({[this.getSuffix(key)]: obj[key]})));
   // Return the part after the last "." (or the whole name if it doesn't contain ".")
   getSuffix(name): string {
      return name.replace(/^.*\./, '');

This code uses some Regex to find the part of a name after the last "." and applies this to all the VM object keys.

At this point the plugin app should compile normally but no changes are visible yet. You can verify in Chrome that myplugin UI still displays correctly.  If you see a compile error it should come with some indication of the file and line number where you can verify your changes.



Prepare the VM Monitor View

Now that we have a VmService, let’s add the HTML content and wire up the Monitor view.  The first step is to add the Dev mode elements, header and sidenav, to integrate the view with the rest of the standalone app, as well as the app-alert component to report service errors.




Open vm-monitor.component.html in myplugin-ui\src\app\views\vm-monitor

Replace the previously generated code with the following lines:

vm-monitor works!


On line 5 *ngIf="gs.showDevUI()  means that the app-header component is only displayed when showDevUI() returns true.  

Likewise, in line 16, *ngIf="gs.showSidenav() means that the sidenav component is only displayed when showSidenav() returns true.

Variable gs represents the GlobalsService provided in /shared/globals.service.ts, it gives access to global environment properties.

In order to use gs in the HTML template we must inject it in the VmMonitorComponent constructor:


Open vm-monitor.component.ts in the same directory.

Add the constructor argument public  gs: GlobalsService

Add the import statement for GlobalsService (or click on the light bulb that shows up when GlobalsService is still unknown)



VM Monitor View with Header and Sidenav


In Chrome, go to http://localhost:4201/vm-monitor/vm-10  to see the VM Monitor view updated with the Dev mode header and side navigator.

Next we will add some real content.



Add Visuals to the Data

We want to display CPU and memory stats with simple meter bars as shown below. We will use a Clarity progress bar component to represent these meters.  This is how it will look like when you are done with the next few steps:




Add a View Title with VM Icon


Keep the Chrome tab open at http://localhost:4201/vm-monitor/vm-1 so that you can switch between Chrome and Visual Studio Code to see the effects of your changes.

The title is a div where we include Clarity's own VM icon with <clr-icon shape="vm" size="32">

In vm-monitor.component.html replace the

   vm-monitor works!


Virtual Machine Statistics for mock-VM1

and see the result in the Chrome tab:






Update vm-monitor with Meter Bars

Each bar and its label is a <div class="progress-group"> like this one:

<div class="progress-group">
   <div class="row">
      <div class="col-xs-6">CPU consumed: 2 Ghz</div>
      <div class="col-xs-6 text-right">2.5 Ghz</div>
   <div class="progress-static labeled success">
      <div class="progress-meter" [attr.data-value]="80"></div>

The 4 <div class="progress-group"> are stacked up with <div class="row"> and their width controlled with the standard bootstrap grid formatting.

<div class="row">
    <div class="col-lg-8 col-md-8">

<div class="col-lg-8 col-md-8"> means that for desktop size we’ll use 2/3 of the width (8 columns out of 12).

Here is the whole Monitor view content, using hard-coded values:

Virtual Machine Statistics for mock-VM1
Number of CPUs: 1
CPU consumed: 2 Ghz
2.5 Ghz
CPU active: 1.5 Ghz
Memory consumed: 3 GB
4.096 GB
Overhead memory: 2 GB


Open vm-monitor.component.html  in Visual Studio Code.

Paste the whole content <!-- Monitor view content --> provided above, in place of the title inside <div class="content-area">

See the app refresh automatically at http://localhost:4201/vm-monitor/vm-10, you should get the sample display as the screenshot above.



Replace Hard-Coded Values

The next step is to replace the meters' hard-coded values with mock data, and then with service calls.


Before getting real VM data with VmService let’s add a mockVm object in vm.model.ts:

Open vm.model.ts under myplugin-ui\src\app\services

Copy this code at the end of the file:

export const mockVm: VM = {
   id: "vm-10",
   name: "mock-VM1",
   numCPU: 1,
   maxCpuUsage: 2500,
   overallCpuUsage: 2000,
   overallCpuDemand: 1500,
   maxMemoryUsage: 4096,
   consumedOverheadMemory: 2000,
   hostMemoryUsage: 3000

Now we will import the new mockVm in vm-monitor.component.ts and use it to initialize the vm variable displayed in the view.


Open vm-monitor.component.ts

  1. Declare the class variable:
    vm: VM;
  2. Initialize that variable in the constructor:
    this.vm = mockVm;
  3. Add the import statement to make the compiler happy:
    import { VM, mockVm} from "../../services/vm.model";


We also need a function percents to compute the percentage values used by the progress bar meters.

Copy this code after the ngOnInit() function:

// Return CPU and Memory stats in whole integer percentages
percents(vm: VM) {
   return {
      cpuConsumed: Math.round(100 * vm.overallCpuUsage / vm.maxCpuUsage),
      cpuActive: Math.round(100 * vm.overallCpuDemand / vm.maxCpuUsage),
      memConsumed: Math.round(100 * vm.hostMemoryUsage / vm.maxMemoryUsage),
      memActive: Math.round(100 * vm.consumedOverheadMemory / vm.maxMemoryUsage)



Remove Hard-Coded Values in HTML template

With those changes in place in VmMonitorComponent we can replace hard-coded values with the vm variables in vm-monitor.component.html, in blue in the code below:

<!-- Monitor view content -->
<div style="font-size: 22px;padding-bottom: 10px">
   <clr-icon shape="vm" size="32"></clr-icon>
   Virtual Machine Statistics for {{vm.name}}
<div class="row">
   <div class="col-lg-8 col-md-8">
      Number of CPUs: {{vm.numCPU}}
      <div class="progress-group">
         <div class="row">
            <div class="col-xs-6">CPU consumed: {{vm.overallCpuUsage / 1000}} Ghz</div>
            <div class="col-xs-6 text-right">{{vm.maxCpuUsage/1000}} Ghz</div>
         <div class="progress-static labeled success">
         <div class="progress-meter" [attr.data-value]="percents(vm).cpuConsumed"></div>
      <div class="progress-group">
         <div class="row">
         <div class="col-xs-6">CPU active: {{vm.overallCpuDemand / 1000}} Ghz</div>
         <div class="progress-static labeled">
         <div class="progress-meter" [attr.data-value]="percents(vm).cpuActive"></div>
      <div class="progress-group" style="padding-top:20px;">
         <div class="row">
         <div class="col-xs-6">Memory consumed: {{vm.hostMemoryUsage / 1000}} GB</div>
         <div class="col-xs-6 text-right">{{vm.maxMemoryUsage/1000}} GB</div>
         <div class="progress-static labeled success">
         <div class="progress-meter" [attr.data-value]="percents(vm).memConsumed"></div>
      <div class="progress-group">
         <div class="row">
         <div class="col-xs-6">Overhead memory: {{vm.consumedOverheadMemory / 1000}} GB</div>
         <div class="progress-static labeled">
         <div class="progress-meter" [attr.data-value]="percents(vm).memActive"></div>
<!-- End view content -->

Update your version of  vm-monitor.component.html with these values.  The simplest is to paste in the entire code block.

The browser tab at http://localhost:4201/vm-monitor/vm-10 will refresh automatically but you should not see any changes since the values of mockVm are the same!

As an exercise you can also modify some mockVm values in vm.model.ts and see the changes reflected in the UI.



Use VmService for Live Data

Now we can wire up VmService and retrieve the real VM data based on the vm id.



Using a vmData$ Observable


Here are the changes required in vm-monitor.component.ts

(1) Replace the vm class variable with vmData$: Observable<VM>;
    Note: the terminating $ is an Angular naming convention for variables representing observables.

(2) Inject VmService and ActivatedRoute in the constructor, like this:

   constructor(public  gs: GlobalsService,
            private vmService: VmService,
            private route: ActivatedRoute) { 

(3) Initialize the vmData$ observable in the constructor with this code:

      this.vmData$ = this.route.paramMap.pipe(
         map(paramMap => paramMap.get('id')),
         switchMap(id => this.vmService.getVmProperties(id, vmStatProperties))

Note that no data will be retrieved in the constructor.  The vmData$ observable is "cold" until it gets "unwrapped" in vm-monitor.component.html (see next step below). This implementation extracts the VM "id" parameter on the route URL and use it to call getVmProperties, returning a VM type.

(4) Declares the vmStatProperties constant as the array of VM properties we are interested in:

const vmStatProperties = [
      "name", "config.hardware.numCPU", "runtime.maxCpuUsage",
      "summary.quickStats.overallCpuUsage", "summary.quickStats.overallCpuDemand",
      "runtime.maxMemoryUsage", "summary.quickStats.consumedOverheadMemory",

(5) Finally add the necessary imports at the top to make the compiler happy:

import { Observable } from 'rxjs';
import { tap, map, switchMap } from 'rxjs/operators';
import { VmService } from '../../services/vm.service';
import { ActivatedRoute } from '@angular/router';


- Observable is the preferred pattern to display data into views when the data comes from a source of events (user navigation and asynchronous http calls): there is no risk to get the wrong vm data, and each event can be cancelled cleanly.

- ActivatedRoute is an Angular API used to get details on the current component route, i.e. URL.



Unwrap the vmData$ Observable in the View


The vmData$ observable is “unwrapped” in vm-monitor.component.html with the help of Angular’s async pipe. The effect of this pipe expression is to extract the latest value of vmData$ and expose it as the vm variable in the rest of the HTML template.

Open  vm-monitor.component.html

Change the <div class="content-area" element with this code:

<div class="content-area" *ngIf="vmData$ | async as vm">

After making this changes, see the browser tab at http://localhost:4201/vm-monitor/vm-10, it contains the values for vm-10 instead of the mockVm data used earlier.  

You can also go to http://localhost:4201/vm-monitor/vm-11, http://localhost:4201/vm-monitor/vm-12, etc.





Navigating from Host to VM Monitor

Now that our VM monitor view is working it would be nice to let users navigate from a Host to a VM view. For this we will add a link over each VM name.


Open monitor.component.html

Add this <a> tag around the {{vm.name}} value

   <a (click)="goToVmMonitor(vm.id)" style="cursor: pointer">{{vm.name}}</a>

Note that we are changing the cursor to the pointer style to indicate that the name is clickable.


Function goToVmMonitor needs to be added to the HostMonitorComponent class:

Open monitor.component.ts

Select then copy this code at the end of the class, before the last closing }

// Navigate to the VM monitor view for the given VM id
goToVmMonitor(id: string): void {
   this.nav.showObjectView(id, "vm", "monitor");

The NavService in app/services/nav.service.ts already implements a generic showObjectView function which takes care of routing the application to the correct component.  You can now click on the VM name in the Host > Monitor view and navigate to the corresponding VM Monitor view.

  1. Select mock-Host 1 Monitor view
  2. Click Virtual Machines and see the list: vm-10, vm-11, vm-12
  3. When you mouse over each vm name it becomes a blue link and the cursor becomes a pointer.
  4. Click on vm-11 for instance.  This takes you to the monitor view of mock-VM 2 as shown below.
  5. See how the URL gets updated to http://localhost:4201/vm-monitor/vm-11
  6. The left navigator doesn't change because it doesn't support VM objects, but you can use the browser left and right arrows to go back and forth.






Next Steps

You have reached the end of this chapter.  There are two more things to do to complete this plugin:

  1. Handle errors when the vm data is not available.
  2. Make the new VM Monitor view visible in plugin mode, within vSphere Client.

For instance you can check that clicking on the VMs of other hosts leave the view empty because the mock data is not available (the browser console will show the 404 error).

Also, if you update the plugin code that was installed with vSphere Client in the last module (using the npm run watch script) you can check that the VM names show up correctly in each host, but clicking on a VM navigates to that VM's Summary page, not the new Monitor view.

These two items will be covered in the next section.  You can also resume the lab there at a later time by following the instructions in Steps to Restart from a Specific Section.


Add View in Plugin Mode and Handle Errors

In this section we will improve error handling and complete the plugin changes to test them with vSphere Client.

NOTE: if you are restarting the lab directly from here please follow Steps to Restart from a Specific Section under the Lab Overview and use myplugin-ui-v2.

Go the myplugin tab in Chrome and select mock-Host 2

When you navigate to a non existing vm the view remains blank and there is no visible error either.  We will improve this with some error handling code.



Open the browser Developer Tools through the right-side menu or Ctrl-Shift-I and select the Console tab.

Click on vm-20. The view gets blank because vm-20 doesn't match any mock VM id in db.json, so no data is returned.

See the error 404 Not Found  in the Console tab for request http://localhost:3000/vms/vm-20


New VmError Type

To solve this, let's first add a new VmError type containing a VM id and an error message, it will allow the VmService to return the proper info to the VmMonitorComponent in case of error.

Add this code at the bottom of vm.model.ts:

export class VmError {
   constructor(public id: string, public error: string) {


vm.service.ts will be updated as following:

  1. Inject private errorHandler: AppErrorHandler in the constructor.
  2. Change the return type of function getVmProperties() to Observable<VM | VmError>
    This is an example of the flexibility of Typescript: multiple types can be returned with the "or" operator.
  3. Add a catchError block to handle http errors (the previous line needs to be terminated with ",")
    catchError(error => observableOf(
               new VmError(objectId, this.errorHandler.getHttpClientError(error))))

Note: errorHandler.getHttpError(error) is a utility which converts the raw http error into a friendly message.

4.   Add the proper import statements to make the compiler happy.  For VmError, of, catchError and AppErrorHandler



Report Errors with AppAlertService


Finally, vm-monitor.component.ts can be updated to handle error conditions:

  1. Change the type of vmData$ to Observable<VM | VmError> to match what is returned by getVmProperties()
  2. Inject private appAlertService: AppAlertService in the constructor.
  3. Add a .tap block to detect when vmData is a VmError and not a VM object, and use the built-in appAlertService which displays error messages in a red banner at the top.
   constructor(public  gs: GlobalsService,
            private vmService: VmService,
            private route: ActivatedRoute,
            private appAlertService: AppAlertService) { 
      this.vmData$ = this.route.paramMap.pipe(
         map(paramMap => paramMap.get('id')),
         switchMap(id => this.vmService.getVmProperties(id, vmStatProperties)),
         tap(vmData => {
            if (vmData instanceof VmError) {
               this.appAlertService.showError((vmData as VmError).error);

AppAlertService, VmError and tap must be added to the import list.

NOTE: The tap operator allows to perform actions on Observables for each value being processed, without affecting the stream of data.


The plugin page at http://localhost:4201/vm-monitor/vm-20 is now refreshed with a red alert box showing the missing vm-20.  The page is no longer empty because the vmData$ observable contains some value. However this is not a VM object so no statistics are displayed.

A real application would go one step further and show something better than empty stats :-)

As an exercise, if you have time, you can check how this is done for hosts in monitor.component.html and try to do the same thing for VMs (see the block <ng-template #hostError>)



New VM Monitor View in Plugin Mode

Before the new VM Monitor view can work in Plugin mode, in vSphere Client, there is one last important step: a new extension must be declared in plugin.xml to let the vSphere Client know about this new VM view.

plugin.xml contains all the extension definitions for a plugin, i.e. the metadata for views, menu items, shortcuts, etc. that make up the plugin, and which uses the vSphere Client SDK extension points.


Go back to mock-Host 1 and click on one of its vms to display a valid VM Monitor view

Toggle the View info switch in the header

This opens a light blue ribbon containing the extension id for that view: com.mycompany.myplugin.vm.monitorView

But the Show Details button has no effect because that extension definition still needs to be added to plugin.xml


Open src/webapp/plugin.xml and scroll to Host monitor tab view at line 96.

The new extension is almost identical to this Host monitor extension, so you can do a Copy/Paste and replace host with vm in the first 2 lines.  You must also replace view=monitor by view=vm-monitor in the <url>.

Here is the full xml code for the VM extension:



As soon as plugin.xml is updated with that new extension you can click again on Show Details to check that the <extension> xml code shows up correctly for the VM Monitor view.



Rebuild and Hot-Deploy myplugin

It is not possible to use npm run watch to refresh the plugin UI in vSphere Client in this case because any change in plugin.xml must be parsed by the Client platform runtime.  Instead we will use another script to rebuild myplugin-ui.war  and “hot deploy” it in the Virgo server.



  1. Kill the npm run watch process with Ctrl-C if you still have it running in a Command Line window, or open a new Command Line.
  2. Go to C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui
  3. Run the script tools\deploy-war.bat
  4. You should see BUILD SUCCESSFUL at the end.

By copying the newly built myplugin-ui.war into server/pickup this script uses the hot deployment feature of Virgo to refresh bundles. There is no need to restart the server.


If you open the command line window where you started the vSphere Client runtime earlier you can verify in the server logs that myplugin-ui bundle was redeployed successfully.



Test myplugin in vSphere Client



Go back to the Chrome tab with your local vSphere Client at https://localhost:9443/ui

Select another host in the left navigator to force the refresh of Host > Monitor > myplugin view (or click the browser Refresh button)

Expand Virtual Machines and click on one of them.

The new VM Monitor view gets loaded and the left navigator focus changes to that VM.

NOTE: Only running VMs have non empty stats in this setup


WARNING: If you see the message "Plugin view is not supported by this HTML client" it usually means that the extension data added to plugin.xml contains some invalid characters.  Verify for instance that all spaces are real space characters.


Module 3 - Lifecycle of a vSphere Client Plugin (30 Minutes)


In the previous module you have built and deployed a plugin in a Development environment, now we explore how a plugin is delivered to end-users in a Production environment, using the vCenter extension registration mechanism.

There are 3 steps:

  1. Prepare your plugin for production
  2. Register it as a vCenter extension
  3. Test the deployment in vSphere Client

Don't worry if you didn't finish the previous module, this one restarts from a brand new plugin which is already built.

IMPORTANT: If you are starting Module 2 in a different Lab session than Module 1 there are  installation steps required to get json-server running.  Before starting this module please follow the instructions for the first three chapters of Module 1:

Plugin Deployment Overview


When the user logs into the vSphere Client the plugin is requested from the vCenter, downloaded from the registered URL and deployed into vSphere Client application server.  Here are the detailed steps:

  1. The plugin package .zip file is hosted on the plugin provider's server.
  2. The vCenter administrator has registered the plugin using that hosted location URL
  3. During a user login the vSphere Client asks vCenter for the list of its registered plugins.
  4. If vSphere Client finds a plugin that hasn't been deployed yet (or is a new version) it downloads using the registered URL
  5. vSphere Client first performs compatibility checks
  6. Then the plugin bundles can be "hot deployed" on the vSphere Client runtime (Virgo server)
  7. The plugin UI becomes visible in the Client for all future users.

Plugin Lifecycle


Preparing the plugin for release to production


In the first module, under Deploy the Plugin to vSphere Client, you were asked to build the plugin package and deploy it with the local vSphere Client. This was done with the script HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui\tools\build-plugin-package.bat.  You can see the output of that script in the File Explorer under HOL-1911\vSphereClientPlugin\vsphere-client-sdk\vsphere-ui\plugin-packages\myplugin.

The folder myplugin contains the following:

- plugin-package.xml: the xml manifest containing the plugin ID, version, general information and compatibility constraints.

- plugins/: the set of bundles containing the plugin code and 3rd party libraries.  In this case we have one UI bundle, myplugin-ui.war, one Java service bundle, myplugin-service.jar, and the library gson-2.3.1.jar.



Remove myplugin if it is deployed locally

At this time, if you have deployed myplugin locally as shown in the picture above, please delete that directory.  In File Explorer use right-click on directory myplugin and click Delete, or enter this command in a terminal window:

rmdir /s C:\LabFiles\HOL-1911\vSphereClientPlugin\vsphere-client-sdk\vsphere-ui\plugin-packages\myplugin


NOTE: Removing an existing test version of myplugin deployed locally is necessary to avoid a conflict when we deploy the same plugin as a production version.


After deleting myplugin directory you also need to restart the Virgo server to remove the plugin from runtime memory.

  1. Find the terminal window with the Virgo server process is running and type Ctrl-C to kill it.
  2. Use the Up arrow to bring back the command used to start it, i.e  %VSPHERE_SDK_HOME%\vsphere-ui\server\bin\startup.bat  
  3. It will take a couple minutes but you can move on to the next steps without waiting for this to complete.

NOTE: If you haven't started the server earlier in this lab, open a new command prompt and enter this command:




Update the plugin manifest


Find the myplugin-solutions/prebuilt directory in File Explorer, right-click on plugin-package.xml and use Edit with Notepad++.

This is the plugin "manifest" where you can define the name, description and vendor values that will appear in the list of plugins for vSphere Client.


The <dependencies> section contains:

(1) The minimal version of vSphere Client supported by the plugin at line 27.

(2) An additional line 30 which restricts this plugin to the vSphere Client (HTML).

<pluginPackage id="com.vmware.vsphere.client.html" version="6.7.0" />

NOTE: By default HTML plugins built with vSphere Client SDK 6.7 are compatible only with vSphere Client (HTML) while HTML plugins built with vSphere Web Client SDK 6.7 are compatible with both vSphere Client (HTML) and vSphere Web Client (Flash). As the vSphere Web Client (Flash) and SDK have been deprecated and vSphere Client (HTML) is the default user choice you may want to limit your plugin to support only that version.  The main benefits are to avoid testing the plugin with the old client, and to take advantage of SDK improvements developed only for vSphere Client (HTML). Such exist since 6.5 U1 - check the SDK release notes for each version for more information.



Make the plugin package available for download


First we compress the content of prebuilt into a new .zip file that we call myplugin.zip

  1. In File Explorer select both the plugins directory AND the plugin-package.xml with Shift-Click
  2. Right click on the selection and select Send to
  3. Select Compressed (zipped) folder
  4. Name the file myplugin.zip

NOTE: It is important NOT to compress the prebuilt directory itself, but its content.




Next we move myplugin.zip to a location where it can be served by a http server.  In a production environment that server would be the plugin vendor's own appliance.  In this lab we are re-using the json-server introduced in Module 1, it is already set up to serve all static files under myplugin-ui\src\webapp

Drag myplugin.zip to that location in File Explorer or enter this command in a terminal window:

move myplugin.zip ..\..\myplugin\myplugin-ui\src\webapp



Verify URL for myplugin.zip


If you still have the terminal window running the json-server process from Module 1 you don't have anything to change.

Otherwise open a new command prompt and enter:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\myplugin\myplugin-ui
npm run json-server

json-server makes the static files avaible at http://localhost:3000/


Verify that the plugin package is available at http://localhost:3000/myplugin.zip by entering that URL in a new browser tab.

You should see myplugin.zip being downloaded as shown above.



Registering your plugin as a vCenter extension

The SDK includes a tool to register plugins with vCenter. It is available in vsphere-client-sdk\tools\vCenter plugin registration


Open a new Command Prompt and enter this command:

cd C:\labfiles\HOL-1911\vSphereClientPlugin\vsphere-client-sdk\tools\vCenter plugin registration 

The directory vCenter plugin registration contains a default version of the registration tool in prebuilt and a customizable version in project.


Copy the following command and execute it, it should end with message "successfully registered" as shown above:

prebuilt\extension-registration.bat -action registerPlugin -k com.mycompany.myplugin -v 1.0.0 -url https://vcsa-01a.corp.local/sdk -u administrator@corp.local -p VMware1! -pu http://localhost:3000/myplugin.zip

NOTE: since it is a long command it may easier to drag it into a Notepad window first, and then copy it in the Command Prompt window.

NOTE: The INFO message about not using https is there to remind you that the plugin download location should always use https in a production environment.  We only used http here for convenience in our dev environment.



vCenter Extension Manager


You can verify that the vCenter extension was created correctly by using vCenter's built-in MOB  (Managed Object Browser)

  1. Open a browser tab at https://vcsa-01a.corp.local/mob/?moid=ExtensionManager
  2. Use the same credentials as usual: administrator@corp.local, VMware1!
  3. Click on (more...) to expand the list of extensions and see extensionList["com.mycompany.myplugin"] at the bottom
  4. Click further to review the extension details.



Deploy the registered plugin from vCenter


Make sure the local vSphere Client no longer contains our test plugin as shown above.


Open a new Command Prompt and start the vSphere Client:


We're ready to check the "production" deployment workflow.  Now that the plugin is registered with vCenter all that is left to do is for one user to login into vSphere Client.


Open a browser tab at https://localhost:9443/ui and accept the certificate.


Enter the standard HOL credentials or select Use Windows session authentication and hit Login.



Virgo events log


Bring back the terminal window with the Virgo server process. The event logs show the plugin's three bundles being installed and started on the fly, while the server is running. The last line reads Deployed plugin package 'com.mycompany.myplugin' version '1.0.0'.

This hot-deployment mechanism allows to add, update or remove plugins without having to restart the server and affecting current users. Other vSphere Client users will see the newly installed plugin after they login again.

NOTE: A more detailed server log is available at vsphere-client-sdk\vsphere-ui\server\serviceability\logs\vsphere_client_virgo.log.  This is the log to look at in case something goes wrong, either during the download of the plugin package or during the deployment of its bundles.



Local cache of plugins


The deployment of myplugin happened during the first user login after the plugin registration took place. From that point on myplugin is visible to all other new user sessions. Also, if the server is ever restarted there is no need to download myplugin.zip again because it is cached locally in the folder com.mycompany.myplugin-1.0.0 as displayed above.

That folder has the same content as myplugin.zip, plus the file com.mycompany.myplugin-1.0.0.par which is an artifact generated during deployment.



myplugin deployed in production


See the Navigation entry and the shortcut icon for myplugin.

Click on either of them to open the plugin's Main view.

Navigate to a host or a VM in the left navigator and see myplugin Monitor view.

This is the same plugin that you built earlier in the lab but it is now using the proper production deployment mechanism.  We can verify this by going to the list of registered plugins.



Client Plug-Ins View


  1. Click Administration in the left navigator or the Home menu.
  2. Select Client Plug-Ins in the Administration menu
  3. The newly installed myplugin is visible at the top of the list of Client plugins.

NOTE: The plugin fields are populated from the plugin's vCenter registration data. If a certain optional value is missing it is taken from the plugin-package.xml.

The plugin state confirms that the plugin is deployed, enabled and operational.


Plugin Upgrade

In this section we will go through the plugin upgrade cycle by just changing the plugin version and vendor.

We don't need to change the plugin content itself to demonstrate the upgrade mechanism. The only thing that matters for vSphere Client to replace an existing plugin is to find a registered plugin with the same id and a higher version number.


Updating plugin vCenter registration


Bring back the terminal window where you ran the extension-registration script earlier and enter the following command:

prebuilt\extension-registration.bat -action updatePlugin -k com.mycompany.myplugin -v 1.0.1 -url https://vcsa-01a.corp.local/sdk -u administrator@corp.local -p VMware1! -pu http://localhost:3000/myplugin.zip -c MyCompany

The only differences with the registration command are -action updatePlugin,  -v 1.0.1, and -c MyCompany. We're updating the existing plugin registration, changing its version to a higher number and adding a vendor name "MyCompany".



Deploying updated plugin


In order to see the new plugin version you must login again in the vSphere Client.  The login process will trigger the download of the new plugin version.

The terminal window for the Virgo server process shows the undeployment of myplugin version 1.0.0 and the deployment of version 1.0.1.

You can also verify that the local cache contains the new version at C:\ProgramData\VMware\vCenterServer\cfg\vsphere-client\vc-packages\vsphere-client-serenity\com.mycompany.myplugin-1.0.1


Navigate again to the Administration > Client Plug-Ins view to observe that the updated plugin version is now active.



Thank you for participating in the VMware Hands-on Labs. Be sure to visit http://hol.vmware.com/ to continue your lab experience online.

Lab SKU: HOL-1911-07-SDC

Version: 20181104-125432