CustomStateMachines Documentation

Bruno Gillet edited this page Jun 27, 2018 · 5 revisions

CustomStateMachines

Within CloudForms, as a service is provisioned or retired, automate entry points may be freely chosen from service catalog item design. However, as it comes to provisioning or retiring VM(s) related to this service, it goes by default to the same entry points. By default, All VMs share the same provisioning/retirement entry points whatever is their specific context or workflow.

This brick will cover a way to dynamically map VMs State Machines based on context criterias, both for provisioning and retirement, both for Infrastructure and Cloud VMs.

CustomStateMachines have been currently tested on the following release :

  • CloudForms 4.2
  • CloudForms 4.5
  • CloudForms 4.6

Initial tested features were :

  • Infrastructure VM provisioning and retirement on RHV and VMware environments.
  • Cloud VM provisioning and retirement on OpenStack (OSP 10) and AWS EC2.

Use cases / detailed presentation

There may be a lot of them : from rationalizing dev/qa/pre-prod/prod environments up to implementing dedicated Ansible Embedded workflows in VM post-provisioning or retirement State Machines (CF 4.5+).

Here are some of them :

  • Several people working at VM state machine level, needing to have dedicated states without disturbing others.
  • As services may be provisioned with pre-requisites (i.e. IPAM, CMDB,...) ability to still use VM Lifecycle raw provisioning, or other ways if required.
  • Specifying dedicated VM provisioning workflows based on typology while reducing complexity of huge StateMachines with lot of messages maintenance (i.e VM level Ansible Embedded workflows)
  • Quick partial rollbacks without impacting all provisionings done on the platform (State Machine switching by tagging).

By default, domains may be overloaded. So a class/instance may mask and replace any others of the same name within domains hierarchy.

In example, an A and a B domains may exist, and may be switched/enabled/disabled in the hierarchy so that the target state machine may be elected from the highest priority active domain. However, only one of them may be called at a time, and will be called independently of the VM context or it's possibly specific workflows requirements. If A domain is active at highest level, then, there won't be default ways calling the B state machines that A overload at that time. In case we need to have different ways provisioning VMs at the same time we have no options.

If different VM typologies exist (i.e. different infrastructure contexts or workflows) the only eligible state machine at a time will have to implement all logics to check what have to be done or not. In time, that may lead to complex and hard to maintain solution.

This brick proposes a way to address the point by selecting VM StateMachines depending on dynamic criterias.

As a VM is provisioned/retired, a contextual item will be checked that will automatically re-vectorize the intended action to a dedicated State Machine. Context criterias may be whatever you are aware of within Automation, matching your project requirements. It could be a tenant, a template component value, a tag, or whatever else you can grab and base your logic on.

In this implementation, we will consider tags setted on VMs catalog items pointing at names for classes and/or instances to the target state machines.

So, back to our use case, if we have specific requirements or typologies for domain A and B VMs, we will simply require to tag the catalog items of those VMs accordingly so they trigger appropriate StateMachines. We won't have to care or interfere with regular domains hierarchy. As far as State Machines entry point names (of our choice) are unique they will be called for targeted VMs, within their specific context.

Catalog items that are not specifically tagged will still be managed following default policy, so this will be totally transparent to them. Finally, this implementation covers both Infrastructure and Cloud VM provisioning and retirement.

Quick Start Guide

A word about naming conventions

Discussions arose within the community about naming conventions for classes, instances and methods. Initial convention was that classes and instances were named in CamelCase (i.e. MyClass, MyInstance) whereas methods were named in camel_case (ie my_action). As well Class and Methods should have the same name if possible, that should be (theoretically), if a method is not used by several instances. New convention says that classes should remain CamelCase whereas instances should now be snake_case, such as methods are.

This implementation has been initiated some time ago and implement the original convention where classes and instances are both named in CamelCase. As we define state machines names from tags (that do not support upper case characters) we currently translate them from snake to camel case for both. So, if targeting a class/instance named as MyClass and MyInstance, tags values will need to be my_class and my_instance. Evolving to the new convention is not an issue from the technical side (one code line and one condition), but from the functional side it is somewhat intrusive (existing instances renaming) and would mean that behaviour between class and instance name build would not be homogeneous.

Feel free to share your mind if you have some constructive thoughts about this point.

Targeted Integration

We will assume that we want to set-up a dedicated set of Infrastructure State Machines for a specific VM Catalog Item provisioning and retirement. The exact same principles would apply to Cloud VM provisioning (replacing Infrastructure paths with Cloud paths). As well, this brick is transparent to the provisioning kind (template or clone). In our example we will provision from template.

The infrastructure provisioning/retirement namespace have not to be changed. They will remain /Infrastucture/VM/Provisioning/StateMachines/ for provisioning, and /Infrastructure/VM/Retirement/StateMachines/ for retirement.

In the following, those respective namespaces are considered default prefixes on all paths where it applies.

So, in those respective namespaces, default State Machines for our example are :

  • VMProvision_VM/template for provisioning (instance depends on miq_provision.provision_type).
  • VMRetirement/Default for retirement.

In our case, we do not want to go through those default StateMachines, but to set up dedicated ones in the respective namespaces as follows :

  • Custom2/Custom2StateMachine for provisioning.
  • CustomRetirement/Custom2Retire for retirement.

We intentionally choose to have non coherent naming policy between our provisioning and retirement Classes/Instances so to avoid confusion in that example.

Step 1 : Creating your StateMachines

Create your StateMachines classes and instances in one of your test domains. Don't forget populating your new classes with respective update_provision_status and update_retirement_status methods, each accepting a status parameter.

Here is an example of a test datastore snapshot with that step completed.

CSM_001

Step 2 : Create your tags

In that example we will need two tags per state machine as we want to drive provisioning and retirement, with both specific classes and instances. This is not an obligation as you may perfectly wish (in exemple) to use default retirement, omitting any retirement tag. As well you may use default Class values, therefore only specifying instance tags.

However, for full coverage example , we will create categories and tags for the class and the instance.

  • For target classes, tag categories are vm_prov_class for provisioning, vm_ret_class for retirement. Create them if you don't yet have them.
  • For target instances, tag categories are vm_prov_instance for provisioning, vm_ret_instance for retirement. Same notice as before.

As a convention, we considered Classes and Instances to be named in CamelCase. (see previous naming convention discussion). The tags pointing at them will have to be *snake_case *due to tags restrictions in supported character sets so a conversion will be done. To point at Custom2StateMachine, the tag we need to create will require the value custom2_state_machine, and so forth.

The categories/tags to create in our case are :

  • vm_prov_class/custom2
  • vm_prov_instance/custom2_state_machine
  • vm_ret_class/custom_retirement
  • vm_ret_instance/custom2_retire

Step 4 : Tag your catalog item.

Create a catalog item for VM provisioning. Run a test provisioning and retirement. It should currently go through default StateMachines (see thereafter for logs to check). Once you are sure you preserved default behaviors and that provisioning works correctly, tag your new item so to re-vectorize it to your custom provisioning, retirement or both state machines.

Here is an example catalog item, tagged to be re-vectorized on our 2 specific exemple state machines, modifying both class and instance names in each case :

CSM_002

Provision and retire a VM from this catalog item. Check it goes through your custom State Machines (see thereafter for logs to check).

Checking provisioning / retirement selection

Without entering implementation details that are covered later on, you may check for one method output in automation.log : launch_state_machine. This method does class and instance selections before launching appropriate StateMachine. Here is an awk’ed output of launch_state_machine on our example service.

Provisioning output :

> <AEMethod launch_state_machine> ---> launch_state_machine starting
> <AEMethod launch_state_machine> --> Called with message : provision - class tag category : vm_prov_class
> <AEMethod launch_state_machine> --> got service_template : BrickCustomStateMachine - ID: 64000000000014
> <AEMethod launch_state_machine> --> Called with message : provision - instance tag category : vm_prov_instance
> <AEMethod launch_state_machine> --> got service_template : BrickCustomStateMachine - ID: 64000000000014
> <AEMethod launch_state_machine> --> Setting prov. state_machine class to: Custom2
> <AEMethod launch_state_machine> --> Target VM prov. StateMachine = /Infrastructure/VM/Provisioning/StateMachines/Custom2/Custom2StateMachine
> <AEMethod [/CustomStateMachines/Integration/CloudForms/CustomStateMachines/launch_state_machine]> Ending

Retirement output :

> <AEMethod launch_state_machine> ---> launch_state_machine starting
> <AEMethod launch_state_machine> --> Called with message : retire - class tag category : vm_ret_class
> <AEMethod launch_state_machine> --> got service_template : BrickCustomStateMachine - ID: 64000000000014
> <AEMethod launch_state_machine> --> Called with message : retire - instance tag category : vm_ret_instance
> <AEMethod launch_state_machine> --> got service_template : BrickCustomStateMachine - ID: 64000000000014
> <AEMethod launch_state_machine> --> Setting ret. state_machine class to: CustomRetirement
> <AEMethod launch_state_machine> --> Target VM ret. StateMachine with instance = /Infrastructure/VM/Retirement/StateMachines/CustomRetirement/Custom2Retire
> <AEMethod [/CustomStateMachines/Integration/CloudForms/CustomStateMachines/launch_state_machine]> Ending

As well, you may include a log output of the current state machine you are in from either your update_provision_status or update_retirement_status code within your custom state machines by adding the following at the beginning of those methods :

$evm.log(:info," ===> Current VM provisioning StateMachine : #{$evm.current_namespace}/#{$evm.current_class}/#{$evm.current_instance}")

Implementation details

Default logic

As a request event is sent, one initial entry point used to select the appropriate StateMachine (for infrastructure) is under the class /Infrastructure/VM/Lifecycle for Infrastructure VMs or /Cloud/VM/Lifecycle for Cloud VMs.

The principle being the same for Infrastructure and Cloud VM provisioning or retirement, we will consider Infrastructure in the following discussion.

The Lifecycle class schema define an empty list of states, mainly relationships and methods. This class contains two instances that are of interest in our case : Provisioning and Retirement . The third one, Migrate, do not enter in our current perimeter.

Provisioning

Here are the states used in Lifecycle/Provisioning instance :

CSM_003

At state RelationShip5 it calls an instance named from the user group name within /Infrastructure/VM/Provisioning/Profile class, transmitting the message get_state_machine. By default, the only group that has an instance is EvmGroup-super_administrator with no specificities from the class schema, so in most cases it’s the .missing instance that will be called, identical as well to the class schema by default. In those instances the only step that is triggered on the get_state_machine message is the last one :

CSM_004bis

We then have the choice to intercept the default provisioning StateMachine election at two different stages, at the Lifecycle/Provisioning or at the Profile/.missing stage.

Retirement

Here is the state used in Lifecycle/Retirement default instance :

CSM_004

From that state we directly jump to the default retirement StateMachine.

Implementing custom StateMachine selection

To stay homogeneous in our implementation we decided to place our StateMachines selection and launching entry point at the same level for both provisioning and retirement. Therefore we will implement it at the Lifecycle/Provisioning step. From provisioning standpoint it means the default feature of selecting a state machine based on the user group will be deactivated. So far I never seen it used nor did I see use cases for it to be used. Should it happen, then the presented logic based on a tag criteria could perfectly be adapted to match a group criteria.

The CustomStateMachines class

Logic is implemented at : /CustomStateMachines/Integration/CloudForms/CustomStateMachines

CSM_005

This class contains two instances and one method. The two instances inherit all their values from the class schema except the following :

  • Instance InfraStateMachine: Initialize basic paths and defaults for Infrastructure VM provisioning. Execute launch_state_machine

  • Instance CloudStateMachine: Initialize basic paths and defaults for Cloud VM provisioning. Execute launch_state_machine

The method is the same for provisioning and retirement, both for Cloud or Infrastructure VMs.

Method launch_state_machine : Will determine the state machine class to be called and will run it. This method have to be called with a message provision when provisioning, retire when retiring. If there are custom class or instance tags on the catalog item it will integrate them in the StateMachine path (translating from snake_case to CamelCase). If there are no such tag, it will use the default class or instance, or both, in place.

To avoid hard coding and improve maintenance and customization, the instance schema is somewhat complete but should not require modification except if you really wish it. Here is this schema from the InfraStateMachine instance.

CSM_006

We considered that values had to be set only when relevant to the message sent. Names are self explanatory but here is a short who_does_what :

  • [provision,retirement]_category : The tag categories to search for classes names.
  • [prov,ret]_instance_cat : The tag categories to search for instances names.
  • def_prov_* : The default MiQ values for provisioning (should not be changed). Note that def_prov_namespace is different between InfraStateMachine and CloudStateMachine.
  • def_ret_* : The default MiQ values for retirement (should not be changed) Note that def_ret_namespace is different between InfraStateMachine and CloudStateMachine
  • [prov,ret]_msg : The messages the instance accept (should not be changed)

Vectorizing StateMachines selection and launch

Logically, we modified the previously seen Lifecycle instances. They now contain only one state, so to call the appropriate CustomStateMachine selection. All messages are default create messages from class schema.

/CustomStateMachines/Infrastructure/VM/Lifecycle/Provisioning

CSM_007

/CustomStateMachines/Infrastructure/VM/Lifecycle/Retirement

CSM_008

/CustomStateMachines/Cloud/VM/Lifecycle/Provisioning

CSM_009

/CustomStateMachines/Cloud/VM/Lifecycle/Retirement

CSM_010

From now, as far as those four instances may be called for provisioning or retirement (meaning, they are not overloaded at some place) you will have dynamic custom state machines activated on all your enabled domains, whatever is their priority order.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.