Call Workflows from outside

0

LittleCMDB (An Orchestrator and WaveMaker project) – Part 1

Table of Content

In Part1 we start with the SQL DB Plugin and create the required database for our need.

In Part2 we start with the development of our Workflow. We will start with a few elements.

In Part3 we  finish the  collection of the VM information.

In Part4 we insert our data into the database and test our created workflow

In Part5 we create our webview to get a look on our Data in the SQL Database

In Part6 we will make our Workflow smarter to update the DB with actual VM information

In Part7 problems with vAPP located virtual machines are fixed

Part1

Today I want to start with a series of posts for an example how to to create an Orchestrator and Wavemaker project. This project came from a “real-world” situation. The customer wants his VM configuration saved outside the virtual environment into a SQL Database. During the talks with the customer, the idea for a little CMDB was born. The actual customer project is much bigger than the example in this article series, but you will become a good insight view on all relevant topics and themes.

When we think about a CMDB we have to choose which data we want to save in our database. Here are some things I included in my database:

  • Unified Identifier (VMID) (The primary key for the VM)

  • VM UUID (the second key for the VM)

  • VM name

  • CPU Configuration

  • Memory Configuration

  • Cluster

  • Host

  • Folder

  • Resource Pools

  • Network

  • IP Address

  • Datastore(s)

  • Disk Size

Surely there are more things which could be integrated but in this post I will focus these values. These identified values bring us to a database schema. Also there are not so many values; I prefer to save the data in different tables. On every table I choose the Unified Identifier with the name VMID as primary key. As second key I select the VMUUID with should be unique for every virtual machine within a vCenter Server. I also split the different information for the VMs in different tables for maybe future grow.

I create the following tables with these values:

VM_Info

Name

Data Type

IsNull

Comments

VMID

varchar(100)

False

PrimaryKey

VMUUID

varchar(100)

False

VMName

varchar(100)

False

CPUConfig

Numeric(18)

False

MemConfig

Numeric(18)

False

VM_Host

Name

Data Type

Is Null Allowed

Comments

VMID

varchar(100)

False

PrimaryKey

VMUUID

varchar(100)

False

Cluster

varchar(100)

True

Host

varchar(100)

False

ResourcePool

varchar(100)

True

Folder

varchar(100)

True

VM_Network

Name

Data Type

Is Null Allowed

Information

VMID

varchar(100)

False

PrimaryKey

VMUUID

varchar(100)

False

Network

varchar(100)

True

Multiple Values Possible

IPAddress

varchar(500)

true

Multiple Values Possible

VM_Datastore

Name

Data Type

Is Null Allowed

Information

VMID

varchar(100)

False

PrimaryKey

VMUUID

varchar(100)

False

DiskSize

varchar(100)

True

Multiple Values Possible

Datastore

varchar(500)

true

Multiple Values Possible

I am not a database Admin and I cannot say if this is a good DB schema, but it fits my needs and I can grow with additional tables.

As Database a use a MS-SQL Server. My DB is named LittleCMDB. Here is the Script to create the DB on an MS-SQL or MS-SQL Express DB.

USE [LittleCMDB]
GO
/****** Object:  Table [dbo].[VM_Network]    Script Date: 07/13/2012 15:33:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[VM_Network](
[VMID] [varchar](100) NOT NULL,
[VMUUID] [varchar](100) NOT NULL,
[Network] [varchar](100) NULL,
[IPAddress] [varchar](500) NULL,
CONSTRAINT [PK_VM_Network] PRIMARY KEY CLUSTERED
(
[VMID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[VM_Info]    Script Date: 07/13/2012 15:33:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[VM_Info](
[VMID] [varchar](100) NOT NULL,
[VMUUID] [varchar](100) NOT NULL,
[VMName] [varchar](100) NOT NULL,
[CPUConfig] [numeric](18, 0) NOT NULL,
[MemConfig] [numeric](18, 0) NOT NULL,
CONSTRAINT [PK_VM_Info_1] PRIMARY KEY CLUSTERED
(
[VMID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[VM_Host]    Script Date: 07/13/2012 15:33:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[VM_Host](
[VMID] [varchar](100) NOT NULL,
[VMUUID] [varchar](100) NOT NULL,
[Cluster] [varchar](100) NULL,
[Host] [varchar](100) NULL,
[ResourcePool] [varchar](100) NULL,
[Folder] [varchar](100) NULL,
CONSTRAINT [PK_VM_Host] PRIMARY KEY CLUSTERED
(
[VMID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object:  Table [dbo].[VM_Datastore]    Script Date: 07/13/2012 15:33:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[VM_Datastore](
[VMID] [varchar](100) NOT NULL,
[VMUUID] [varchar](100) NOT NULL,
[DiskSize] [varchar](100) NULL,
[Datastore] [varchar](500) NULL,
CONSTRAINT [PK_VM_Datastore] PRIMARY KEY CLUSTERED
(
[VMID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

For the usage of a DB in Orchestrator, you can use the SQL-Plugin. The Plug in could be get from the VMware Website (https://my.vmware.com/web/vmware/details?downloadGroup=VCO_SQL_PLUGIN_10&productId=229)

The Installation is done over the Orchestrator Configuration site (http://ORCHESTRATOR_IP:8282). I will not describe the installation. If you are not familiar with the plug in installation then RTFM in the official documentation here http://pubs.vmware.com/orchestrator-plugins/index.jsp?topic=/com.vmware.using.sql.plugin.doc_10/GUID-66110FFD-EB0F-484A-937B-8C131C8DFFB2.html

After you finished the installation of the SQL Plug in, it is time to integrate the SQL Server and the required Database tables.

You can add your database with the vCO Client Workflows Library SQL “Add a database”

There you have to define your SQL Connection Details. In my case, that are the required parameter for the SQL connection. In my case that are:

  • A name for the connection

  • The SQL Server IP with Port and database name (for details look in the screen shot..)

  • A username with permissions on the DB

  • And the password for the user

After the database is successfully added, we have to integrate the preconfigured tables. This is done with the workflow “Add tables to a database”

 

After the start of the workflow we first have to choose the database

In the opening window we take our DB (LittleCMDB in my case…)

 

Then we have to integrate our tables

 

You can add tables by clicking in the “Tables” field

 

there you have to insert all fields beginning with VM_*. Add the end the your Array of String has to look like this:

 

After you have „Accept“ your choice you can „Submit“ the Workflow.

 

Before I start to generate the SQL Statements, I create a Folder in which I will place my SQL-Statements. For that I have created a folder “vcoportal.de” there a sub folder “LittleCMDB”. In the “LittleCMDB” Folder a also created a Subfolder with the name “SQL-Statements”.

Feel free to change your names and folders, i prefer this structure.

To generate your SQL-Statements we have to browse to the Workflow “Generate CRUD workflows for a table”

This Workflow must be executed for every table we want to use.

For that, you have to provide the following inputs:

  • The table

  • The destination directory

  • If you want to overwrite existing workflows

  • And the read-only columns

 

Here is a example for one of my tables:

 

When you are ready for your tables, take a look into your destination folder. For every table, there must be a record to “Insert, Update, Read and Delete” values in your tables.

The preparation of the SQL Server statements is done. Know we can start to build up our workflow to feed the SQL Database…..

So, that’s all for Part1. Stay tuned for Part2. Then we will start to create the Workflow…..

SQL Script
SQL Script
LittleCMDB_SQL.zip
622.0 B
Details...
0

Introducing: The LittleCMDB (a vCenter Orchestrator & WaveMaker Demo-Project)

It is a pleasure for me to write these lines as an introduction to a full series of articles, posted on vcoportal over the next couple of days:

Christian Strijbos (you remember his posts about WaveMaker and vs. LDAP a couple of weeks ago!?) put together a comprehensive project involving vCenter Orchestrator (and its SQL Plugin) and WaveMaker:

***…drumroll…***

The LittleCMDB

***…drumroll…***

Features

The LittleCMDB is a Workflow-driven Database Application which stores information of Virtual Machine configurations in a SQL database. The user can see the configuration via a web-based UI. It’s even possible to file a Request to change the configuration of a VM via the Web-Frontend, it will automatically processed and the settings of the VM will be adjusted.

In short, you can…:

  • Show the current Virtual Machine configuration in a Web-Frontend
  • Initialize the Database automatically
  • Refresh the Database when the configuration of a Virtual Machine changes
  • Let the users change the Virtual Machine configuration via a Web-Frontend

Architecture

LittleCMDB uses Orchestrator Workflows to implement the “business logic” of the application. For that it leverages the SQL-Plugin for vCO, so you don’t have to write a single line of SQL Statements.

There will be some backend Workflows which gather information from vCenter and store them into the Database.

The web-based frontend is built with VMware WaveMaker.

What you get:

We will publish a full guide how-to build the LittleCMDB from scratch, separated in different parts over the next couple of days (see the picture above also as a “Table of Content”).

We will also provide the Workflow Packages and the WaveMaker project to download.

Our main goal is to give you…

  • examples and “hands-on” guides to develop workflows in vCO
  • same for WaveMaker
  • an example how easy it is to create such a “CMDB application” using vCO Plugins
  • ideas to use vCO and WaveMaker for your own use-cases

So: Even if the downloadable workflows should  run quite out-of-the-box, do NOT use them for production!
The complete project is for educational purposes only.
(If you like the idea, and want something like that for your company, engage Christian for a project!  8-))

Now, get your vCO Lab ready and look forward to Part 1…

tomorrow in this Theater :mrgreen:!

Here we go:

Part 1:
http://www.vcoportal.de/2012/07/little-cmdb-part1/

Part 2:
http://www.vcoportal.de/2012/07/littlecmdb-an-orchestrator-and-wavemaker-project-part-2/

Part 3:
http://www.vcoportal.de/2012/07/little-cmdb-part3/

Part 4:
http://www.vcoportal.de/2012/07/little-cmdb-part4/

Part 5:
http://www.vcoportal.de/2012/07/littlecmdb-part-5/

Part 6:
http://www.vcoportal.de/2012/08/littlecmdb-an-orchestrator-and-wavemaker-project-part-6/

Part 7:
http://www.vcoportal.de/2012/08/littlecmdb-an-orchestrator-and-wavemaker-project-part-7/

0

Howto setup LDAP-Authentication for Wavemaker (Part 2)

Remember Part 1:

http://www.vcoportal.de/2012/05/howto-setup-ldap-authentication-for-wavemaker-part-1/

The Workaround

In the solution of Part 1, you can only find groups in one OU. In the wavemaker community, there are some links to additional documentation, were this post is based on.

Let’s have a look to the AD. In the Wavemaker examples a “flat” AD Structure is used

Whow, I have never seen an AD like this….. In the Wavemaker community examples always a “flat” AD environment is used. In such an environment it is easy to setup a Group Authentication…but in the environments, where I work I never had such an easy AD. So let’s have a look to a “real” AD:

When you use Wavemaker to configure your AD you are limited in the GUI. You cannot point your configuration to different OUs. Your only option is to get user out of one OU. But how do you configure your application if your users were sprawl in different OUs?

So when your people are from different OUs you have a problem….
For this problem, there is a solution, but therefore you have to make your LDAP configuration manually. First of all, you have to configure your AD Server connection. I assume that you have done this already. After that, we need some configuration changes…

First we have do modify the file project-security.xml. This is located under:

%PROJECTPATH%\webapproot\WEB-INF\project-security.xml

Insert the following code

<bean id="userSearch">

<constructor-arg index="0">

<value>cn=users</value> --> Here you must put the parent directory were to start the user search

</constructor-arg>

<constructor-arg index="1">

<value>(sAMAccountName={0})</value>

</constructor-arg>

<constructor-arg index="2">

<ref local="initialDirContextFactory"/>

</constructor-arg>

<property name="searchSubtree">

<value>true</value>

</property>

</bean>

One word to the code above, I was not able to start my search onto the root of the domain. I didn’t find a solution to start the search from there! I had to choose a OU or subfolder.

After that, you have to change some code in the file… See the screenshot for the “summary” of all changes, and find the complete before/after .xml-files below.

Be aware: After that, you cannot use the  LDAP configuration page in Wavemaker anymore! If you do so, everything will be overwritten and you have to start from scratch again!

The limitation above is not a Wavemaker limitation but comes from the used ACEGI Security which is used in Wavemaker. Additional information can be found here:

http://www.opennms.org/wiki/Acegi_Security_and_LDAP

So have fun with Orchestrator, Wavemaker and your Active Directory!

Complete before/after-xml-files

Change this…

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <bean id="filterChainProxy">
 <property name="filterInvocationDefinitionSource">
 <value>
 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 PATTERN_TYPE_APACHE_ANT
 /**=httpSessionContextIntegrationFilter,logoutFilter,formAuthenticationProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterSecurityInterceptor
 </value>
 </property>
 </bean>
 <bean id="httpSessionContextIntegrationFilter"/>
 <bean id="logoutFilter">
 <constructor-arg value="/index.html"/>
 <constructor-arg>
 <list>
 <bean/>
 </list>
 </constructor-arg>
 <property value="/j_acegi_logout" name="filterProcessesUrl"/>
 </bean>
 <bean id="formAuthenticationProcessingFilter">
 <property name="filterProcessesUrl">
 <value>/j_acegi_security_check</value>
 </property>
 <property name="authenticationFailureUrl">
 <value>/login.html?login_error=1</value>
 </property>
 <property name="defaultTargetUrl">
 <value>/</value>
 </property>
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 </bean>
 <bean id="jsonExceptionTranslationFilter">
 <property name="authenticationEntryPoint">
 <ref bean="formLoginAuthenticationEntryPoint"/>
 </property>
 </bean>
 <bean id="formLoginAuthenticationEntryPoint">
 <property name="loginFormUrl">
 <value>/login.html</value>
 </property>
 <property name="forceHttps">
 <value>false</value>
 </property>
 </bean>
 <bean id="anonymousProcessingFilter">
 <property name="key">
 <value>sharedsecret</value>
 </property>
 <property name="userAttribute">
 <value>anonymousUser,ROLE_ANONYMOUS</value>
 </property>
 </bean>
 <bean id="anonymousAuthenticationProvider">
 <property name="key">
 <value>sharedsecret</value>
 </property>
 </bean>
 <bean id="filterSecurityInterceptor">
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 <property name="accessDecisionManager">
 <ref bean="accessDecisionManager"/>
 </property>
 <property name="objectDefinitionSource">
 <value>
 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 PATTERN_TYPE_APACHE_ANT
 /*.upload=IS_AUTHENTICATED_FULLY
 /*.download=IS_AUTHENTICATED_FULLY
 /index.html=IS_AUTHENTICATED_FULLY
 /=IS_AUTHENTICATED_FULLY
 /securityservice.json=IS_AUTHENTICATED_ANONYMOUSLY
 /*.json=IS_AUTHENTICATED_FULLY
 </value>
 </property>
 </bean>
 <bean id="authenticationManager">
 <property name="providers">
 <list>
 <ref bean="ldapAuthProvider"/>
 <ref bean="anonymousAuthenticationProvider"/>
 </list>
 </property>
 </bean>
 <bean id="daoAuthenticationProvider">
 <property name="userDetailsService">
 <ref bean="inMemoryDaoImpl"/>
 </property>
 </bean>
 <bean id="inMemoryDaoImpl">
 <property name="userMap">
 <value>
 demo=demo,ROLE_DEFAULT_NO_ROLES
 </value>
 </property>
 </bean>
 <bean id="jdbcDaoImpl">
 <property name="dataSource">
 <ref bean="jdbcDataSource"/>
 </property>
 <property name="usersByUsernameQuery">
 <value>SELECT first_name, last_name, 1 FROM employee WHERE first_name = ?</value>
 </property>
 <property name="authoritiesByUsernameQuery">
 <value>SELECT first_name, "DEFAULT_NO_ROLES" FROM employee WHERE first_name = ?</value>
 </property>
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 <property name="usernameBasedPrimaryKey">
 <value>true</value>
 </property>
 </bean>
 <bean id="jdbcDataSource"/>
 <bean id="ldapAuthProvider">
 <constructor-arg>
 <bean>
 <constructor-arg>
 <ref local="initialDirContextFactory"/>
 </constructor-arg>
 <property name="userDnPatterns">
 <list>
 <value>cn={0},ou=people</value>
 </list>
 </property>
 </bean>
 </constructor-arg>
 <constructor-arg>
 <bean>
 <constructor-arg>
 <ref local="initialDirContextFactory"/>
 </constructor-arg>
 <constructor-arg>
 <value>ou=groups</value>
 </constructor-arg>
 <property name="groupSearchDisabled">
 <value>false</value>
 </property>
 <property name="roleProvider">
 <value>LDAP</value>
 </property>
 <property name="groupRoleAttribute">
 <value>cn</value>
 </property>
 <property name="groupSearchFilter">
 <value>(member={0})</value>
 </property>
 </bean>
 </constructor-arg>
 </bean>
 <bean id="initialDirContextFactory">
 <constructor-arg value="ldap://localhost:389/dc=wavemaker,dc=com"/>
 <property name="managerDn">
 <value>cn=manager,dc=wavemaker,dc=com</value>
 </property>
 <property name="managerPassword">
 <value>7b6a43524a282c146a6b626e425c32205754</value>
 </property>
 </bean>
 <bean id="accessDecisionManager">
 <property name="allowIfAllAbstainDecisions">
 <value>false</value>
 </property>
 <property name="decisionVoters">
 <list>
 <bean/>
 <bean/>
 </list>
 </property>
 </bean>
 <bean id="roleVoter">
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 </bean>
 <bean id="autoProxyCreator">
 <property name="proxyTargetClass">
 <value>true</value>
 </property>
 <property name="interceptorNames">
 <list>
 <value>securityInterceptor</value>
 </list>
 </property>
 </bean>
 <bean id="securityInterceptor">
 <property ref="authenticationManager" name="authenticationManager"/>
 <property ref="accessDecisionManager" name="accessDecisionManager"/>
 <property name="objectDefinitionSource">
 <value>
 </value>
 </property>
 </bean>
 <bean scope="singleton" id="securityService">
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 <property name="noRolesMarkerRole">
 <value>DEFAULT_NO_ROLES</value>
 </property>
 <property name="roles">
 <list/>
 </property>
 </bean>
</beans>

…to this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <bean id="filterChainProxy">
 <property name="filterInvocationDefinitionSource">
 <value>
 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 PATTERN_TYPE_APACHE_ANT
 /**=httpSessionContextIntegrationFilter,logoutFilter,formAuthenticationProcessingFilter,anonymousProcessingFilter,jsonExceptionTranslationFilter,filterSecurityInterceptor
 </value>
 </property>
 </bean>
 <bean id="httpSessionContextIntegrationFilter"/>
 <bean id="logoutFilter">
 <constructor-arg value="/index.html"/>
 <constructor-arg>
 <list>
 <bean/>
 </list>
 </constructor-arg>
 <property value="/j_acegi_logout" name="filterProcessesUrl"/>
 </bean>
 <bean id="formAuthenticationProcessingFilter">
 <property name="filterProcessesUrl">
 <value>/j_acegi_security_check</value>
 </property>
 <property name="authenticationFailureUrl">
 <value>/login.html?login_error=1</value>
 </property>
 <property name="defaultTargetUrl">
 <value>/</value>
 </property>
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 </bean>
 <bean id="jsonExceptionTranslationFilter">
 <property name="authenticationEntryPoint">
 <ref bean="formLoginAuthenticationEntryPoint"/>
 </property>
 </bean>
 <bean id="formLoginAuthenticationEntryPoint">
 <property name="loginFormUrl">
 <value>/login.html</value>
 </property>
 <property name="forceHttps">
 <value>false</value>
 </property>
 </bean>
 <bean id="anonymousProcessingFilter">
 <property name="key">
 <value>sharedsecret</value>
 </property>
 <property name="userAttribute">
 <value>anonymousUser,ROLE_ANONYMOUS</value>
 </property>
 </bean>
 <bean id="anonymousAuthenticationProvider">
 <property name="key">
 <value>sharedsecret</value>
 </property>
 </bean>
 <bean id="filterSecurityInterceptor">
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 <property name="accessDecisionManager">
 <ref bean="accessDecisionManager"/>
 </property>
 <property name="objectDefinitionSource">
 <value>
 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 PATTERN_TYPE_APACHE_ANT
 /*.upload=IS_AUTHENTICATED_FULLY
 /*.download=IS_AUTHENTICATED_FULLY
 /index.html=IS_AUTHENTICATED_FULLY
 /=IS_AUTHENTICATED_FULLY
 /securityservice.json=IS_AUTHENTICATED_ANONYMOUSLY
 /*.json=IS_AUTHENTICATED_FULLY
 </value>
 </property>
 </bean>
 <bean id="authenticationManager">
 <property name="providers">
 <list>
 <ref bean="ldapAuthProvider"/>
 <ref bean="anonymousAuthenticationProvider"/>
 </list>
 </property>
 </bean>
 <bean id="daoAuthenticationProvider">
 <property name="userDetailsService">
 <ref bean="inMemoryDaoImpl"/>
 </property>
 </bean>
 <bean id="inMemoryDaoImpl">
 <property name="userMap">
 <value>
 demo=demo,ROLE_DEFAULT_NO_ROLES
 </value>
 </property>
 </bean>
 <bean id="jdbcDaoImpl">
 <property name="dataSource">
 <ref bean="jdbcDataSource"/>
 </property>
 <property name="usersByUsernameQuery">
 <value>SELECT first_name, last_name, 1 FROM employee WHERE first_name = ?</value>
 </property>
 <property name="authoritiesByUsernameQuery">
 <value>SELECT first_name, "DEFAULT_NO_ROLES" FROM employee WHERE first_name = ?</value>
 </property>
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 <property name="usernameBasedPrimaryKey">
 <value>true</value>
 </property>
 </bean>
 <bean id="userSearch">
 <constructor-arg index="0">
 <value>cn=users</value>
 </constructor-arg>
 <constructor-arg index="1">
 <value>(sAMAccountName={0})</value>
 </constructor-arg>
 <constructor-arg index="2">
 <ref local="initialDirContextFactory"/>
 </constructor-arg>
 <property name="searchSubtree">
 <value>true</value>
 </property>
 </bean>

<bean id="jdbcDataSource"/>
 <bean id="ldapAuthProvider">
<constructor-arg>
 <bean>
 <constructor-arg>
 <ref local="initialDirContextFactory"/>
 </constructor-arg>
 <property name="userSearch">
 <ref bean="userSearch"/>
 </property>
 </bean>
 </constructor-arg>
 </constructor-arg>
 <constructor-arg>
 <bean>
 <constructor-arg>
 <ref local="initialDirContextFactory"/>
 </constructor-arg>
 <constructor-arg>
 <value>ou=groups</value>
 </constructor-arg>
 <property name="groupSearchDisabled">
 <value>false</value>
 </property>
 <property name="roleProvider">
 <value>LDAP</value>
 </property>
 <property name="groupRoleAttribute">
 <value>cn</value>
 </property>
 <property name="groupSearchFilter">
 <value>(member={0})</value>
 </property>
 </bean>
 </constructor-arg>
 </bean>
 <bean id="initialDirContextFactory">
 <constructor-arg value="ldap://localhost:389/dc=wavemaker,dc=com"/>
 <property name="managerDn">
 <value>cn=manager,dc=wavemaker,dc=com</value>
 </property>
 <property name="managerPassword">
 <value>7b6a43524a282c146a6b626e425c32205754</value>
 </property>
 </bean>
 <bean id="accessDecisionManager">
 <property name="allowIfAllAbstainDecisions">
 <value>false</value>
 </property>
 <property name="decisionVoters">
 <list>
 <bean/>
 <bean/>
 </list>
 </property>
 </bean>
 <bean id="roleVoter">
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 </bean>
 <bean id="autoProxyCreator">
 <property name="proxyTargetClass">
 <value>true</value>
 </property>
 <property name="interceptorNames">
 <list>
 <value>securityInterceptor</value>
 </list>
 </property>
 </bean>
 <bean id="securityInterceptor">
 <property ref="authenticationManager" name="authenticationManager"/>
 <property ref="accessDecisionManager" name="accessDecisionManager"/>
 <property name="objectDefinitionSource">
 <value>
 </value>
 </property>
 </bean>
 <bean scope="singleton" id="securityService">
 <property name="authenticationManager">
 <ref bean="authenticationManager"/>
 </property>
 <property name="rolePrefix">
 <value>ROLE_</value>
 </property>
 <property name="noRolesMarkerRole">
 <value>DEFAULT_NO_ROLES</value>
 </property>
 <property name="roles">
 <list/>
 </property>
 </bean>
</beans>
0

Howto setup LDAP-Authentication for Wavemaker (Part 1)

You can leverage VMware Wavemaker to create a nice web-frontend for your users to start workflows.
(how? See the links at the end of this article!)

Typically you don’t let just everybody do that, but only identified users. So you have to implement some kind of User Authentication mechanism on the web-frontend.

The Good

Wavemaker provides included mechanisms to implement a login page, and authenticate the users against a database or a LDAP-directory, using a so called Security Service. This LDAP authentication also works against Active Directory.

The Security Service also allows a group-based distinction into different roles: you can show or hide different parts of the website to users, depending if they are in an AD-group or not.

This works very well, and is (as usual for Wavemaker  :-)) very well documented:
http://dev.wavemaker.com/wiki/bin/Authentication
http://dev.wavemaker.com/wiki/bin/SecurityTutorial

The Bad

The normal login page (automatically created when you activate this checkbox in the security service) does not allow you to re-use the user credentials “later” on webpage. But, you might need these credentials to authenticate against vCO for workflow execution. Then you have to rebuild the authentication mechanism manually, and create a JavaService to check if the user is in a specific LDAP-group.

Let’s see, how to do:

First of all, you have to configure the connection to your LDAP/AD Server. The easiest ways to do so is over the Wavemaker GUI.

 You can insert the LDAP/AD integration in the menu Services / Security.

 On the site you have to choose your Security Provider and to enable Security.

Important here:

  • Do NOT check the “Show Login Page”-checkbox! You will create your own login screen later…
  • Check the “Search User Role”-checkbox to activate the mechanism which does the association to LDAP-groups
***Very Useful Site Note***
You can test your settings using the “Test Connection”-Button. (Even this is not vCO-related (yet 😉 ): If you get an error, remember what you learned from vCO configuration 😀 e.g. the “Administrator” as Manager DN in AD is identified as “cn=Administrator,cn=Users,dc=lab,dc=local”
For troubleshooting: I’m sure you remember this post on how to troubleshoot LDAP issues?
http://www.vcoportal.de/2011/07/troubleshooting-ldap-erros-in-vco/ 
***Another Useful Site Note***
As frequent reader of this blog you remember adfind: A small, very powerful, very fast, very handy tool to figure out where and how to find objects in Active Directory…

To implement the group-checking manually you have to insert a JavaService into your project.

In the menu choose Services / Java Service:

 

In the popup you must enter a name and a package name for the java service.

 

In my case I choose the Service Name GroupAuth and LDAPGroupAuth.

Here is the java source code for that:

/**
 * This is a client-facing service class. All
 * public methods will be exposed to the client. Their return
 * values and parameters will be passed to the client or taken
 * from the client, respectively. This will be a singleton
 * instance, shared between all requests.
 *
 * To log, call the superclass method log(LOG_LEVEL, String) or log(LOG_LEVEL, String, Exception).
 * LOG_LEVEL is one of FATAL, ERROR, WARN, INFO and DEBUG to modify your log level.
 * For info on these levels, look for tomcat/log4j documentation
 */
 import com.wavemaker.runtime.RuntimeAccess;
 import com.wavemaker.runtime.security.SecurityService;

public class LDAPGroupAuth extends com.wavemaker.runtime.javaservice.JavaServiceSuperClass {
 /* Pass in one of FATAL, ERROR, WARN, INFO and DEBUG to modify your log level;
 * recommend changing this to FATAL or ERROR before deploying. For info on these levels, look for tomcat/log4j documentation
 */
 public String LDAPGroupAuth() {
 log(INFO,"ldapauth...");
 String LoginTrue= "Yes";
 String LoginFalse= "No";
 if (isUserInRole("VCOADMINS")){

 return LoginTrue;
 }else{

 return LoginFalse;
 }
 }

 private static boolean isUserInRole(String role){
 SecurityService srv = (SecurityService) RuntimeAccess.getInstance().getService("securityService");
 String[]uRoles = srv.getUserRoles();
 for(String uRole : uRoles){
 // log(INFO,"role: " + uRole);
 if(uRole.equals(role)){
 return true;
 }
 }
 return false;
 }

}

Important here: The name of the AD-group has to be typed in UPPERCASE!

Next, I had to create the “flow” of the login process, using different Layers in Wavemaker.

I copy the “Login Page” Box and the scripts into my Main site. There I work with different layer and I am able to use the “variables” on all layers.

For the integration of your GroupAuth into your project, you have to modify our “Login” Button.
During my tests for the project, I searched for a good solution to check the credentials (Username and password) against the AD Server and validate the group membership in one step. I didn’t find a, for me, robust solution.
If someone finds a better solution than the one below, please comment!  😎

Due that circumstance, I decided to use a temporary Auth_Layer. By clicking on the “Login” button, I check the credentials, on the Login site and after checking these where valid I check the group membership.

So, here is the code for checking the AD credentials:

loginButtonClick: function(inSender) {
dojo.cookie("user", this.usernameInput.getDataValue(), {expires: 365});
 this.loginErrorMsg.setCaption("");
 wm.login(
 [this.usernameInput.getDataValue(), this.passwordInput.getDataValue()],
 dojo.hitch(this, "loginSuccess"), dojo.hitch(this, "loginFailed"));
 },
 loginSuccess: function(inResponse) {
 try{
 // This will set the currently showing layer to the AuthLayer
 main.layers1.setLayer('Auth_layer');

 } catch(e) {
 console.error('ERROR IN loginSuccess: ' + e);
 }
 },
 loginFailed: function(inResponse) {
 this.loginErrorMsg.setCaption("Invalid username or password.");
 this.usernameInput.focus();
 },

When the user credentials where valid, the layer is changed to the “Auth_Layer”.

On this layer, the group membership is checked. Here is the code for the check (executed in the “onShow()”-Event of this Layer):

loginButtonClick: function(inSender) {
 dojo.cookie("user", this.usernameInput.getDataValue(), {expires: 365});
 this.loginErrorMsg.setCaption("");
 wm.login(
 [this.usernameInput.getDataValue(), this.passwordInput.getDataValue()],
 dojo.hitch(this, "loginSuccess"), dojo.hitch(this, "loginFailed"));
 },
 loginSuccess: function(inResponse) {
 try{
 // This will set the currently showing layer to the AuthLayer
 main.layers1.setLayer('Auth_layer');

 } catch(e) {
 console.error('ERROR IN loginSuccess: ' + e);
 }
 },
 loginFailed: function(inResponse) {
 this.loginErrorMsg.setCaption("Invalid username or password.");
 this.usernameInput.focus();
 },

So if the group membership of the user is okay, the user is redirected to the “Request_Layer”. If the group membership is not okay, the user is redirect to the “Denied_Layer”.

In sum, the system provides for three different conditions:

  • Username or Password is wrong: This shows an small “login unsuccessful”-message directly in the login-box
  • Username / Password is correct (so he is logged-in from the Wavemaker Security Service perspective), but the user is NOT in the AD-group. Then he’s redirected to the “Denied_Layer”.
  • Username / Password is correct (so he is logged-in from the Wavemaker Security Service perspective), AND he is in the AD-group. Then he’s redirected to the “Request_Layer”, which for instance contains the buttons that trigger Orchestrator Workflows.

The Ugly

Given some restrictions of the underlying mechanisms used for the Security Service implementation in Wavemaker, it only supports groups within one Organizational Unit (OU) in Active Directory.
However, there is a workaround for this, expect Part 2 of this article soon :mrgreen:

References

To give you an idea about it looks like at the end of the day, download the project below!
(It’s a Wavemaker 6.4 exported project, should be directly importable. Just make sure to adjust the LDAP-Settings of the Security Service!)

Aaaaand, some links:
http://www.vcoportal.de/2011/11/using-wavemaker-as-web-frontend-for-vco/
http://www.virtuallyghetto.com/2011/12/leveraging-vcd-vco-wavemaker-part-1.html
http://www.virtuallyghetto.com/2011/12/leveraging-vcd-vco-wavemaker-part-2.html
http://dev.wavemaker.com/wiki/bin/Learning/Mastering+LDAP+Security

Wavemaker-LDAP-Authentication
Wavemaker-LDAP-Authentication
vCO_WM_LDAP.1.Alpha.zip
160.2 KiB
Details...
0

The Dream of a self-healing Datacenter: Integrate vCenter Operations (vCOPS) with Orchestrator

Workflows in vCenter Orchestrator allow you to automate tasks in vCenter. That’s “Kindergarten“.

Workflows also allow you to orchestrate IT-Services all over the infrasctructure, leveraging all that generic or specific plugins (Wanna see the list? Go to VMware’s Solution Exchange: https://solutionexchange.vmware.com/store/category_groups/15/categories/21).
That’s the “Advanced to Master“-Level.

Kicking-off “healing” workflows based on “unhealthy” conditions in your datacenter fully-automated, using vCO as a headless orchestration platform? That sounds like a job for Wizards!
Well, let’s see……

Orchestrator Wizard (bearded)

The Basics

What is VMware vCenter Operations?
Quoting http://www.vmware.com/products/datacenter-virtualization/vcenter-operations-management/overview.html:
“… Automate performance, capacity, and configuration management with patented analytics and an integrated approach to management. Eliminate the finger pointing, improve team collaboration and reduce manual problem solving efforts by as much as 40% with automated root cause analysis… blah blah Marketing blah 🙄 ”

In short: It’s great! 

VMware vCenter Operations (vCOPS)


What do we need in vCenter Orchestrator?
Just some basic facts
: Orchestrator Workflows are triggered mostly in one of these three ways:

  • Manually: By an administrator (using the vCO-Client), or an Helpdesk-Agent (using the weboperator Webview, or the Perspectives-Plugin) or  a customer/end-user (via a self-service portal (built in Wavemaker of course  ;-))
  • As Scheduled Task in vCO: The workflow will be started by the vCO server on a regular base, e.g. for reporting, checking for exhausting snapshots, …… (It’s even possible to schedule a workflow programmatically, see a nice example here: http://communities.vmware.com/thread/318791)
  • By an external System: Another component of your Infrastructure kicks-off the workflow via the API. This could be an high-level Business-Process engine (like VMware Service Manager), a system to manage classroom-environments, a release management system, an I/S/P/X/Y/Z-aaS Manager, whatever…

And there is a fourth, not so well known way to start a workflow:

Policies in Orchestrator to start Workflows on signals

  • Event based: The workflow is started when a specific event occurs . You can either use a “Waiting event”-Element in the workflow, or you can create a Policy in vCO. For both vCO-Plugins can provide Triggers.
    Examples:  The AMQP-Plugin (which can be triggered e.g by a Blocking Task in vCloud Director), or the SNMP-Plugin (listens to SNMP-Traps, sent e.g. from vCenter or other systems…)
    Waiting Event in a workflow

Subtotal:

vCenter Operations (vCOPS) can send SNMP-Traps when unwanted situations occur.

vCO can start workflows when an SNMP-Trap is received.

So: Integrate (again)!

Step 1:  Install the SNMP-Plugin on your vCO-Server. RTFM on https://www.vmware.com/support/pubs/vco_plugins_pubs.html

Step 2: Run the Workflows Library / Trap Host Management / Set the SNMP trap port and Start the trap host to make vCO (exactly: the SNMP-Plugin) listen to SNMP traps.

Step 3: Run the Workflow Library / Device Management / Register an SNMP device and make vCO listen to SNMP traps exactly from vCOPS. Make sure you use the hostname / IP of the vCOPs Analytics VM, not the UI VM!

Run Workflows to setup the environment

Step 4: Configure vCOPS to send SNMP-Traps to the Orchestrator server. Open the vCOPS Manager Administration, and define your vCO-Server as (receiving) Host.

Enable SNMP notifications in vCenter Operations

Step 5: Configure vCOPS  to “activate” alerting: Open the menu “Notifications” in vCOPs usual webinterface (not the Administration anymore), and create a “New Rule” and enable all the Conditions you want to be notified on.
(I’m not sure if this step is really necessary,  I didn’t find any information if these rules are for email only, or if they are also used for SNMP notifications 😕 )

Create Rules to enable alerting

Step 6: For a first test: Run the Workflow Library / SNMP Wait for a trap on an SNMP device. Select the vCOPS Analytics VM as SNMP Device, you registered it in Step 3!
The Workflow should stall at the “Waiting Element” for an SNMP trap from vCOPS. Now break something in your datacenter 😈 , so that vCOPS sends a trap…

Waiting for something bad...

Once the trap is received by vCO, the workflow should continue and finish successfully. In the Logs-Tab of the Workflow-Token you can find some details about the trap.

Step 7To be the wizard: Create a new Policy based on the Policy Template which comes with the SNMP Plugin.
Apply a new  Orchestrator Policy

Then edit the created Policy to select the Workflow to be started when a trap is received:

Step 8: Start the Policy! (That’s something you will forget only once, after hours of senseless troubleshooting…)

DON'T FORGET TO START THE POLICY!

Finito!

Now every condition in your datacenter which leads to an alerting in vCenter Operations will trigger a Workflow in Orchestrator, which can deal with the problem. And the best: All that without writing a single line of code!

From there I leave it to you…

<site note>
Integrating a Systems Monitoring tool with vCenter Orchestrator is not a new:
up.time software provides a vCO plugin for years:
http://support.uptimesoftware.com/orchestrator.php
http://www.uptimesoftware.com/uptimeblog/uncategorized/cloud-computing-and-popular-culture/
</site note>

The Dream of a self-healing Datacenter

Proofing that it’s possible, consider what you can do (or better: what you can let do fully-automated) with an integration of vCenter Operations and vCenter Orchestrator:

  • Run a workflow that automatically creates a Ticket or updates the CMDB on vCOPS alerts
  • A datastore runs out of capacity? A workflow will fix it automatically, increase the LUN on the storage system and the VMFS partition. Or just full-stack provision a new datastore and add it to the storage cluster.
  • YourMightyStorageVendor(tm) provides an additional workflow: If now your storage system runs out of capacity, new disks will automatically be ordered by the workflow 🙂
  • Response time for Outlook users to high? A workflow will deploy new Exchange CAS instances to scale-out (and leverage the Powershell-Plugin to adjust it settings)
  • Your web-app is getting “Slashdotted”? See how Radware already leverages vCO to scale it fully-automated: http://www.youtube.com/watch?v=4rkV3ebQens
  • “No worries, Captain! I already put 80% energy to the front shields to prepare for the Klingonian attack!”
  • With the brand-new vCenter Operations for VMware View and a View API/Plugin for vCO you could… … …

***RING – RING – YOUR WAKEUP CALL – RING – RING ***

SIGH! What a nice dream…  :mrgreen: