The author of this blog is Ramona Renner,
Technical Solutions Specialist with the GVE DevNet team at Cisco.
The GVE DevNet Team offers programmability expertise and
custom sample code development to demonstrate the power of
Cisco APIs and SDKs.
Welcome to part 6 of the journey to Vanilla ISE, a simplified ISE GUI for endpoint technicians. In part 1, we covered the background and requirements for the UI. In part 2, we explored the documentation to find the relevant API calls we will use. In part 3, we focused on interacting with network devices and CLI parsing using pyATS. Part 4, is about voucher execution. In part 5, we started the discussion around the frontend and therein described the collaboration between developers.
In this part 6, we will go into more detail about the web application and especially the frontend development. We will explore different languages, libraries, frameworks, and some simplified Vanilla ISE code.
Let’s start with the basics to ensure that everyone is on the same page.
HTML (Hypertext Markup language) is used to define the structure
and content of a web page. It provides the fundamental building
CSS (Cascading Style Sheet) allows to style a page by controlling
the presentation, formatting and layout of the elements defined
which simplifies the use of JS.
It is possible to create your frontend from scratch by using these languages. Still, nowadays it is more common to use libraries and frameworks that provide reusable code and extensions for typical operations. These frameworks and libraries take a lot of work off our hands, but usually still require you to master the languages mentioned above.
For Vanilla ISE we used the Cisco UI Kit and Flask web framework.
Cisco UI Kit is an initiative from the Cisco Brand team to simplify the design and development of applications with Cisco corporate design. Therefore, it provides not only design libraries, but also different developer libraries and the OneUI Tool. OneUI is a drag & drop web design tool that allows to generate GUI code based on the available developer libraries.
Although it is handy to use OneUI, we decided to use the HTML5 developer library and to manually build our web pages. The reason for this was that we wanted to minimize redundant code for recurring UI components. The mentioned HTML5 library combines exactly the languages we explained in the beginning of this blog post – HTML5, CSS and jQuery.
While the Cisco UI Kit eases the development of the web pages itself, it is only one part of the story. For the other part, we used Flask to create a web application based on Oren’s backend code.
Flask is a lightweight web application framework and Python module that simplifies the development of web applications. It is a good choice, especially for new web application developers, since it is easy to get started with. Flask itself incorporates the Jinja2 template engine, Werkzeug toolkit and other modules. The former allows us to define and render pages dynamically. The latter provides functionality to talk with a WSGI server and includes a basic development server. Check out the Flask documentation for more details and features.
For our Vanilla ISE project we used the included development server. (Please be aware that this server is not intended to be used in production.)
Details about the Vanilla ISE code
Since we are now all on the same page, let’s dig into the details about the Vanilla ISE code and see how all the languages, libraries, and tools come together.
A brief note upfront. It is recommended to create a virtual environment for each of your projects to prevent unwanted dependency interactions between projects. The Flask documentation offers detailed information on how to create a virtual environment and install Flask within it.
In the simplest form a Flask application can consist of one basic python file (see image). This file imports the Flask module, creates the app, defines all routes, and uses the built-in development server to run the app. Whereby, routes are used to map a URL path to an associated python function.
Based on a successfully installed Flask module we can run our small Flask project with the python app.py command, whereby app.py is the name of our python file.
Our example will call the index function and display “Hello Cisco!” as soon as we navigate to http://localhost:5000.
While the basic structure of our Vanilla ISE app.py file looks similar to the code above, we added more functionality and a more sophisticated Graphical User Interface. Therefore, we included further code and files and required a file structure for our Flask project. The next image shows our simplified file structure. All files which define the content and structure of our dynamic pages are gathered in a dedicated templates folder. All static files, like the images, css-, js- and font files, are organized in subfolders in the “static” folder. A json file to store and retrieve voucher data is saved in the data folder. Most of the code, that Oren described in the previous blog posts, is saved in separate Python files and referenced in the app.py.
Flask allows to easily integrate a Graphical User Interface. In the next section we will focus on jinja2 HTML templates and the Cisco UI Kit.
A jinja2 HTML template contains static HTML code in combination with jinja2 variables, expressions, and statements. The jinja2 elements are replaced and executed when the template is rendered and thus allow the definition of dynamic page elements. Hereby, the variables can be passed from the app.py code to the template. The statements and expressions are similar to regular Python. The following example shows simplified jinja2 code that we used for Vanilla ISE.
As mentioned before, we used the Cisco UI Kit to build the HTML code of our jinja2 templates. The HTML5 developer kit provides the typical Cisco UI elements as HTML, CSS and jQuery code. You can choose and combine the ready to use components to build a web page according to your requirements. The documentation shows all the available elements and their associated code. Sample pages not only help to understand how the elements can be used in combination, but also provide ready to use page layouts.
Vanilla ISE splits the template code into several files. On the one hand, we created a file for each of the four main functionalities of Vanilla ISE. On the other hand, we added further files to separately define the application frame and specific UI elements that are uses on multiple pages. By doing so, we minimized redundant code and ended up with three different template page types:
- Content pages contain the content area of a page – login, device list, device query, vouchers and endpoint query.
- Master page includes the frame of our application and therefore defines the header and footer element for all content pages.
- Component templates define a single UI component and are reused in multiple content pages – menu, alert.
The described code separation is possible due to the template inheritance functionality of jinja2. The block element allows the definition of a child template that replaces a placeholder in the parent template during rendering. The following pictures show an example of how we used blocks in our Vanilla ISE code.
Beside blocks we furthermore used the include tag. This tag inserts the menu template at a specific position in each content page. No further jinja2 syntax is needed in the menu template file itself.
So far, we discussed a minimal flask application and jinja2 templates with Cisco design. Now, we will explore the connection between our python backend functions and the frontend. Therefore, we will investigate HTML links, forms, and flask routes.
Users typically interact with a web page in two ways. They navigate to a specific page and display information, or they fill out a form on a page to submit information. Following we cover both methods.
For the first method we use HTML links and flask routes. In the frontend, the href attribute of the <a> tag defines the URL path of the page to call. In the backend, the route decorator maps the URL path to a python function and therefore specific actions. Typical actions are the rendering of a template or the retrieving of data to be displayed via API. For a valid mapping the route decorator includes the URL path and HTTP method. At this point we only want to retrieve data from the backend and thus use the GET method.
The second way utilizes HTML forms and Flask routes to send input data from the frontend to our python backend. On the frontend side, a <form> element allows to gather the input data for one or more enclosed <input> fields. The data is sent to the backend as soon as the form submit button is triggered. Each input field must define a name attribute that is used to access the value of the field in the backend. Furthermore, the form element and route decorator must specify the already mentioned action and the method. The POST method is used to send data to the backend for further processing.
Thank you for sticking with us up to this point. We truly hope that the detailed explanation of the thought process and steps we’ve taken creating Vanilla ISE will encourage you to begin your journey into programmability. If you’re already on your journey, then engage a programmability project and overcome a challenge you’re facing. And if you already solved a problem others might be facing – share it with the community on the DevNet Code Exchange.
Cisco DevNet offers you many resources to get you there, completely free of commitment or cost.
- Learn more about Cisco Identity Services Engine (ISE)
- Visit the DevNet Security Dev Center – to learn how Cisco Security supports third-party integration across its portfolio with 18+ open APIs and integration points.
We’d love to hear what you think. Ask a question or leave a comment below.
And stay connected with Cisco DevNet on social!
Visit the new Developer Video Channel