How to Add an AngularJS Dashboard Plugin to OpenStack Horizon

Author: Cindy Lu
Source: Planet OpenStack

Give me Liberty! Since the Kilo release, Horizon, the OpenStack Dashboard has made a big push for writing new features and functionality in AngularJS, an MVC Javascript framework. The decoupling of presentation logic and business logic helps promote dynamic UIs that designers who aren’t programmers can markup, have easy access to data, parallel development, scalability, usability, readability, reusability, and testability.

One of the key goals moving forward is to maintain its extensibility for old and new installations. The Horizon plugin framework has been built out to support Django and Angular-based plugins.

As a developer, I’ve wondered the process for writing a standalone plugin for the Horizon dashboard from scratch. Here is how I did it and some tips and tricks along the way. There is by no means a comprehensive guide and may be out of date in the future, follow with caution! The complete example can be found here and used as a reference for this post.

Part 1: How to Build it

If you want to build a new dashboard in Django, please follow this guide. Since the new release, we have built a foundation for building custom dashboards and panels in AngularJS, which is not well-documented.

However, you can take a look at existing dashboards as a reference for how to write an AngularJS dashboard. They are still built on top of Horizon’s Django foundation, so you can still use the boilerplate code generated by the commands in the tutorial. The first thing you will notice is that Angular-related files are all stored inside the static directory (e.g. openstack_dashboard/dashboards/project/static/) within each dashboard. It is up to you on how you want to structure the files as long as they are located here, because Horizon will pick up all the files from this directory. We want to create a table, so let’s create a table directory inside the panel directory.

Here is how the AngularJS files would look.

my_dashboard/static/dashboard/my_dashboard
	├── my_dashboard.module.js
 	├── my_dashboard.module.spec.js
	├── my_panel/
 	│  └── table/
 	│      └── table.controller.js
 	│      └── table.controller.spec.js
	│      └── table.html
 	├── my_panel.module.js
 	└── my_panel.module.spec.js

Now inside openstack_dashboard/dashboards/my_dashboard/my_panel/templates/my_panel/index.html, we have an entry point to table.html.

       <ng-include src="'{{ STATIC_URL }}dashboard/my_dashboard/my_panel/table/table.html'"></ng-include>

You will also created in new file in the openstack_dashboard/enabled folder. Let’s take a look at an enabled file for the dashboard (e.g. _3000_identity.py or the one you created). This is where Horizon’s pluggability comes into play. Pluggable settings are stored in these separate files so as to not modify the default settings. They are read at startup. A plugin’s settings file will be stored in openstack_dashboard/local/enabled. The following options are available within each settings file:

ADD _EXCEPTIONS
ADD_INSTALLED_APPS
ADD_ANGULAR_MODULES
ADD_JS_FILES
ADD_JS_SPEC_FILES
ADD_SCSS_FILES
AUTO_DISCOVER_STATIC_FILES

For an Angular-based dashboard, we will use ADD_ANGULAR_MODULES to list all the modules to inject into the application.

In Liberty, we added the setting, AUTO_DISCOVER_STATIC_FILES. If set to “True“, it will automatically discover all the Javascript files and static HTML template files in the static folder in each app listed in ADD_INSTALLED_APPS. Otherwise, you must list all the necessary files in ADD_JS_FILES to be loaded on each page. Likewise, files listed in ADD_JS_SPEC_FILES are loaded when the Jasmine test suite runs.

Run the server.

After the dashboard is fully functional, you can now pull it out from trunk into a plugin. Make sure all your import references are relative. For example, if we look at openstack_dashboard/dashboards/identity/users/views.py, we see an absolute path:

    from openstack_dashboard.dashboards.identity.users import forms

In our plugin, we would simply have:

    from my_panel import forms

Part 2: How to Package it

Now that we have our content all set, we want to package it as a third-party module. We need to write a setup script to describe the package. We use setuptools which is a packaging library extended from Python standard library distutils which helps us compile, distribute, and install Python packages.

Lay out your files like below. The files in bold are the new files to include.

my_plugin
	├── enabled
 	├── my_dashboard
 	│  └── __init__.py
 	│  └── …
	│  └── … <whatever_else_in_the_plugin>
	│  └── …
	├── README
 	├── MANIFEST.in
 	└── setup.py

The distribution will have the name “my_plugin.” The actual package is the “my_dashboard”. This directory must contain __init__.py. “enabled” simply holds the file (_<num>_<my_dashboard_name>.py) that needs to be copied into the trunk in order for Horizon to recognize this plugin.

README
README file is where you put all the details about your application. It is not required.

MANIFEST.in
It is the file that distutils uses to collect all the files in your project that will go into the final tarball. There are few types of commands in this file. Common ones:
include – pick up file relative to the distribution root
recursive-include – pick up files recursively under the given path

Example:

    include setup.py
    recursive-include my_dashboard/templates *
    recursive-include my_dashboard/static *
    recursive-include my_dashboard/my_panel/templates *

setup.py
You will populate setup() function with package metadata (e.g. name, version, description, author, author_email) and information about what’s in the package.

Example:

    from setuptools import setup, find_packages

	setup(
    	    name = 'my_dashboard',
    	    version = '0.0.1',
    	    description = 'sample AngularJS dashboard extension for OpenStack Dashboard',
    	    author = 'Cindy Lu',
    	    author_email = 'clu@us.ibm.com',
    	    packages=find_packages(),
    	    include_package_data = True,
	)

Once you have these three files set up, inside the my_dashboard folder, run “python setup.py sdist”. sdist creates an archive file (tarball in Unix, ZIP file in Windows) containing the setup script and the package. The installation tarball is based off the name and version, so based on the example above, our file is called my_dashboard-0.0.1.

This command will go through all the files in your MANIFEST.in and build the tar.gz file in a folder called dist off the root. If successful, it will give the message:


creating dist
creating tar archive
removing ‘my_dashboard-0.0.1’ (and everything under it)

Now your file structure will look like this:

my_plugin
	├── dist
	├── enabled
 	├── my_dashboard
 	│  └── __init__.py
 	│  └── …
	│  └── …
 	├── my_dashboard.egg-info
 	├── MANIFEST.in
	├── README
 	└── setup.py

The directories in bold are newly generated.

Part 3: How to Test it

Now we want to install this third-party module to make sure everything works. We now have the tar.gz inside the dist folder. Inside the horizon project directory, run the command

    ./tools/withvenv.sh pip install <path_to_plugin>/my_dashboard-0.0.1.tar.gz

This will install the package inside the virtual environment. You can check that all the files are generated properly by going to .venv/lib/site-packages/my_dashboard-0.0.1. Sometimes you may have forgotten to include some files in the MANIFEST.in. You may have to go back and fix it.

To enable it in Horizon, copy the enabled file from my_plugin/enabled/ to openstack_dashboard/local/enabled/.

When you restart the server, you should see your new dashboard!

If it doesn’t show up as expected, the first thing to do is check that all files were packaged correctly (.venv/lib/site-packages/my_dashboard-0.0.1). That way, you can trace it back to which point needs to be fixed.

Part 4: Final Notes

You can now distribute the tar.gz file, publish on GitHub, or publish the third-party plugin on PyPI.

Resources:
Angular Plugin Example: https://github.com/clu-7/horizon-plugin
Horizon Pluggable Settings: http://docs.openstack.org/developer/horizon/topics/settings.html#pluggable-settings
Horizon Plugins: http://docs.openstack.org/developer/horizon/plugins.html

The post How to Add an AngularJS Dashboard Plugin to OpenStack Horizon appeared first on IBM OpenTech.

Powered by WPeMatico