├── .gitignore ├── LICENSE ├── README.md ├── SUMMARY.md ├── book.json ├── chapter1 ├── classes_schemas_and_instances.md ├── domains_and_namespaces.md ├── images │ ├── datastore.png │ ├── datastores_cfme_54_55.graffle │ │ ├── data.plist │ │ ├── image2.tiff │ │ └── image3.tiff │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png ├── methods.md └── the_automation_datastore.md ├── chapter10 ├── images │ ├── screenshot1.png │ ├── screenshot2.png │ └── screenshot3.png └── ways_of_entering_automation.md ├── chapter11 ├── catching_and_handling_external_events.md ├── creating_and_processing_internal_events.md ├── event_driven_automation.md ├── event_processing.md ├── event_processing_botvinnik.md ├── event_switchboard.md ├── evm.log_starting_a_rhev_vm_event_flow ├── extending_event_handling.md └── images │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot13.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot17.png │ ├── screenshot18.png │ ├── screenshot19.png │ ├── screenshot2.png │ ├── screenshot20.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png ├── chapter12 ├── images │ ├── screenshot1.png │ ├── screenshot2.png │ ├── screenshot3.png │ └── state_machine_logic.png └── state_machines.md ├── chapter13 ├── images │ ├── screenshot1.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ └── screenshot6.png └── more_advanced_schema_concepts.md ├── chapter14 ├── automation_operations.md ├── distributed_automation_processing.md └── requests_and_tasks.md ├── chapter15 ├── approval.md ├── create_provision_request.md ├── customising_vm_provisioning.md ├── images │ ├── provisioning_objects.graffle │ ├── provisioning_objects.png │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot13.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot17.png │ ├── screenshot18.png │ ├── screenshot19.png │ ├── screenshot2.png │ ├── screenshot20.png │ ├── screenshot21.png │ ├── screenshot22.png │ ├── screenshot23.png │ ├── screenshot24.png │ ├── screenshot25.png │ ├── screenshot26.png │ ├── screenshot27.png │ ├── screenshot28.png │ ├── screenshot29.png │ ├── screenshot3.png │ ├── screenshot30.png │ ├── screenshot31.png │ ├── screenshot32.png │ ├── screenshot33.png │ ├── screenshot34.png │ ├── screenshot35.png │ ├── screenshot36.png │ ├── screenshot37.png │ ├── screenshot38.png │ ├── screenshot39.png │ ├── screenshot4.png │ ├── screenshot40.png │ ├── screenshot41.png │ ├── screenshot42.png │ ├── screenshot43.png │ ├── screenshot44.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png ├── naming.md ├── options_hash.md ├── placement.md ├── profile_processing.md ├── provisioning_dialog.md ├── provisioning_objects.md ├── provisioning_profile.md ├── quota_management.md ├── scripts │ ├── add_disk.rb │ ├── add_disk_cfme_54.rb │ └── start_vm.rb ├── state_machine.md └── vm_provisioning.md ├── chapter16 ├── ansible_script.yml ├── images │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png ├── integrating_with_satellite_6.md └── scripts │ ├── activate_satellite.rb │ └── register_satellite.rb ├── chapter17 ├── approval_and_quota.md ├── catalogiteminitialization.md ├── creating_a_service_bundle.md ├── creating_a_service_item.md ├── custom_state_machines.md ├── images │ ├── Service Objects 2.graffle │ ├── Service Objects.graffle │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot13.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot17.png │ ├── screenshot18.png │ ├── screenshot19.png │ ├── screenshot2.png │ ├── screenshot20.png │ ├── screenshot21.png │ ├── screenshot22.png │ ├── screenshot23.png │ ├── screenshot24.png │ ├── screenshot25.png │ ├── screenshot26.png │ ├── screenshot27.png │ ├── screenshot28.png │ ├── screenshot29.png │ ├── screenshot3.png │ ├── screenshot30.png │ ├── screenshot31.png │ ├── screenshot32.png │ ├── screenshot33.png │ ├── screenshot34.png │ ├── screenshot35.png │ ├── screenshot36.png │ ├── screenshot37.png │ ├── screenshot38.png │ ├── screenshot39.png │ ├── screenshot4.png │ ├── screenshot40.png │ ├── screenshot41.png │ ├── screenshot42.png │ ├── screenshot43.png │ ├── screenshot44.png │ ├── screenshot45.png │ ├── screenshot46.png │ ├── screenshot47.png │ ├── screenshot48.png │ ├── screenshot49.png │ ├── screenshot5.png │ ├── screenshot50.png │ ├── screenshot51.png │ ├── screenshot52.png │ ├── screenshot53.png │ ├── screenshot54.png │ ├── screenshot55.png │ ├── screenshot56.png │ ├── screenshot57.png │ ├── screenshot58.png │ ├── screenshot59.png │ ├── screenshot6.png │ ├── screenshot60.png │ ├── screenshot61.png │ ├── screenshot62.png │ ├── screenshot63.png │ ├── screenshot64.png │ ├── screenshot65.png │ ├── screenshot66.png │ ├── screenshot67.png │ ├── screenshot68.png │ ├── screenshot69.png │ ├── screenshot7.png │ ├── screenshot70.png │ ├── screenshot71.png │ ├── screenshot72.png │ ├── screenshot73.png │ ├── screenshot74.png │ ├── screenshot75.png │ ├── screenshot76.png │ ├── screenshot77.png │ ├── screenshot78.png │ ├── screenshot79.png │ ├── screenshot8.png │ ├── screenshot80.png │ ├── screenshot81.png │ ├── screenshot82.png │ ├── screenshot83.png │ ├── screenshot84.png │ ├── screenshot85.png │ ├── screenshot86.png │ ├── screenshot87.png │ ├── screenshot88.png │ ├── screenshot89.png │ ├── screenshot9.png │ ├── screenshot90.png │ ├── service_objects_detailed.png │ ├── task_hierarchy.graffle │ └── task_hierarchy.png ├── log_analysis_during_service_provisioning.md ├── object_walker_dump_from_service_provision.txt ├── object_walker_dump_from_service_reconfigure_task.txt ├── satellite_6_api_testing.txt ├── scripts │ ├── activate_satellite.rb │ ├── add_to_service.rb │ ├── get_configuration.rb │ ├── list_activationkeys.rb │ ├── list_hostgroups.rb │ ├── list_hostgroups_from_ems.rb │ ├── list_puppetclasses.rb │ ├── list_services.rb │ ├── list_smart_class_parameters.rb │ ├── register_satellite.rb │ ├── rename_service.rb │ ├── service_dialog_export_generic_server_managed_by_satellite_6.yml │ └── set_configuration.rb ├── service_dialogs.md ├── service_hierarchies.md ├── service_objects.md ├── service_reconfiguration.md ├── state_machine.md ├── tips_and_tricks.md └── working_with_services.md ├── chapter18 ├── images │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png └── retirement.md ├── chapter19 ├── argument_passing_and_handling.md └── images │ ├── screenshot1.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ └── screenshot6.png ├── chapter2 ├── creating_the_class.md ├── exit_status.md ├── hello_world.md ├── images │ ├── circle.graffle │ │ ├── data.plist │ │ └── image39.pdf │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot13.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png └── writing_and_running_our_own_scripts.md ├── chapter20 ├── calling_automation_from_api.md └── scripts │ └── run_via_api.rb ├── chapter21 ├── automation_request_approval.md ├── images │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png └── scripts │ ├── AutomationRequest_Pending.rb │ ├── approve_request.rb │ ├── pending_request.rb │ └── validate_request.rb ├── chapter22 ├── calling_external_services.md └── images │ └── screenshot1.png ├── chapter23 ├── images │ ├── miq_ci_workflow.png │ ├── screenshot1.png │ └── screenshot2.png └── miscellaneous_tips.md ├── chapter3 ├── images │ ├── screenshot17.png │ ├── screenshot18.png │ ├── screenshot19.png │ ├── screenshot20.png │ ├── screenshot21.png │ ├── screenshot22.png │ └── screenshot23.png └── using_schema_object_variables.md ├── chapter4 ├── a_little_rails_knowledge.md ├── distributed_ruby.md ├── images │ ├── general_mvc.png │ ├── mvc.graffle │ │ ├── data.plist │ │ └── image1.png │ └── mvc.png ├── some_background_theory.md ├── the_miqaeservice_model.md └── working_with_automation_objects.md ├── chapter5 ├── a_more_advanced_example.md └── images │ ├── screenshot1.png │ ├── screenshot10.png │ ├── screenshot11.png │ ├── screenshot12.png │ ├── screenshot13.png │ ├── screenshot14.png │ ├── screenshot15.png │ ├── screenshot16.png │ ├── screenshot17.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── screenshot6.png │ ├── screenshot7.png │ ├── screenshot8.png │ └── screenshot9.png ├── chapter6 ├── evm_and_the_workspace.md └── images │ └── object_model.png ├── chapter7 └── using_tags_from_automate.md ├── chapter8 ├── enforce_antiaffinity_rules.md └── scripts │ └── enforce_anti_affinity.rb ├── chapter9 └── investigative_debugging.md └── cover.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .grunt 3 | /_book/ 4 | .tiff 5 | .graffle 6 | .pages 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CloudForms & ManageIQ Automation How-To Guide 2 | 3 | This book has been superseded by the O'Reilly 'Mastering CloudForms Automation" book, available as a free download here: 4 | 5 | [Mastering CloudForms Automation](https://access.redhat.com/mastering-cloudforms-automation) 6 | 7 | The follow-on version from this O'Reilly book, covering CloudForms 4.2 and ManageIQ _Euwe_, is a work-in-progress. It is viewable here: 8 | 9 | [Mastering Automation in CloudForms and ManageIQ](https://www.gitbook.com/book/pemcg/mastering-automation-in-cloudforms-and-manageiq/details) 10 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [The Automation Datastore](chapter1/the_automation_datastore.md) 5 | * [Domains and Namespaces](chapter1/domains_and_namespaces.md) 6 | * [Classes, Schemas, Instances & Relationships](chapter1/classes_schemas_and_instances.md) 7 | * [Methods and Relationships](chapter1/methods.md) 8 | * [Writing & Running Our Own Scripts](chapter2/writing_and_running_our_own_scripts.md) 9 | * [Creating the Class](chapter2/creating_the_class.md) 10 | * [Hello, World!](chapter2/hello_world.md) 11 | * [Exit Status](chapter2/exit_status.md) 12 | * [Using Schema Object Variables](chapter3/using_schema_object_variables.md) 13 | * [Some Background Theory](chapter4/some_background_theory.md) 14 | * [A Little Rails Knowledge](chapter4/a_little_rails_knowledge.md) 15 | * [The MiqAeService* Model](chapter4/the_miqaeservice_model.md) 16 | * [Working with Automation Objects](chapter4/working_with_automation_objects.md) 17 | * [Distributed Ruby](chapter4/distributed_ruby.md) 18 | * [A More Advanced Example](chapter5/a_more_advanced_example.md) 19 | * [$evm and the Workspace](chapter6/evm_and_the_workspace.md) 20 | * [Using Tags from Automate](chapter7/using_tags_from_automate.md) 21 | * [Example - Enforcing Anti-Affinity Rules](chapter8/enforce_antiaffinity_rules.md) 22 | * [Investigative Debugging](chapter9/investigative_debugging.md) 23 | * [Ways of Entering Automation](chapter10/ways_of_entering_automation.md) 24 | * [Event Processing](chapter11/event_processing.md) 25 | * [The Event Switchboard](chapter11/event_switchboard.md) 26 | * [Catching and Handling External Events](chapter11/catching_and_handling_external_events.md) 27 | * [Creating and Processing Internal Events](chapter11/creating_and_processing_internal_events.md) 28 | * [Event-Driven Automation](chapter11/event_driven_automation.md) 29 | * [Extending Event Handling](chapter11/extending_event_handling.md) 30 | * [State Machines](chapter12/state_machines.md) 31 | * [More Advanced Schema Concepts](chapter13/more_advanced_schema_concepts.md) 32 | * [Automation Operations](chapter14/automation_operations.md) 33 | * [Requests and Tasks](chapter14/requests_and_tasks.md) 34 | * [Distributed Automation Processing](chapter14/distributed_automation_processing.md) 35 | * [Provisioning a VM](chapter15/vm_provisioning.md) 36 | * [The Provisioning Profile](chapter15/provisioning_profile.md) 37 | * [Profile Processing - Deep Dive](chapter15/profile_processing.md) 38 | * [Approval](chapter15/approval.md) 39 | * [Quota Management](chapter15/quota_management.md) 40 | * [The Options Hash](chapter15/options_hash.md) 41 | * [The VM Provisioning State Machine](chapter15/state_machine.md) 42 | * [Example - Customising VM Provisioning](chapter15/customising_vm_provisioning.md) 43 | * [VM Naming During Provisioning](chapter15/naming.md) 44 | * [VM Placement During Provisioning](chapter15/placement.md) 45 | * [The VM Provisioning Dialog](chapter15/provisioning_dialog.md) 46 | * [VM Provisioning Objects](chapter15/provisioning_objects.md) 47 | * [Creating Provisioning Requests Programmatically](chapter15/create_provision_request.md) 48 | * [Integrating with Satellite 6 During Provisioning](chapter16/integrating_with_satellite_6.md) 49 | * [Working with Services](chapter17/working_with_services.md) 50 | * [Service Dialogs](chapter17/service_dialogs.md) 51 | * [The Service Provisioning State Machine](chapter17/state_machine.md) 52 | * [Catalog{Item,Bundle}Initialization](chapter17/catalogiteminitialization.md) 53 | * [Approval and Quota](chapter17/approval_and_quota.md) 54 | * [Example - Creating a Service Catalog Item](chapter17/creating_a_service_item.md) 55 | * [Example - Creating a Service Catalog Bundle](chapter17/creating_a_service_bundle.md) 56 | * [Service Objects](chapter17/service_objects.md) 57 | * [Log Analysis During Service Provisioning](chapter17/log_analysis_during_service_provisioning.md) 58 | * [Custom State Machines](chapter17/custom_state_machines.md) 59 | * [Service Hierarchies](chapter17/service_hierarchies.md) 60 | * [Service Reconfiguration](chapter17/service_reconfiguration.md) 61 | * [Service Tips and Tricks](chapter17/tips_and_tricks.md) 62 | * [Retirement](chapter18/retirement.md) 63 | * [Argument Passing and Handling](chapter19/argument_passing_and_handling.md) 64 | * [Calling Automation from the RESTful API](chapter20/calling_automation_from_api.md) 65 | * [Automation Request Approval](chapter21/automation_request_approval.md) 66 | * [Integration - Calling External Services](chapter22/calling_external_services.md) 67 | * [Miscellaneous Tips](chapter23/miscellaneous_tips.md) 68 | 69 | 70 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { "title": "Introduction to CloudForms Automation" } -------------------------------------------------------------------------------- /chapter1/classes_schemas_and_instances.md: -------------------------------------------------------------------------------- 1 | ## Classes, Schemas, Instances & Relationships 2 | 3 | ### Classes 4 | A _Class_ is similar to a template, it contains a generic definition for a set of automation operations. Each Class has a _Schema_, that defines the variables, states, relationships or methods that Instances of the class will use. 5 | 6 | ### Schemas 7 | A _Schema_ is made up of a number of elements, or _fields_. A schema often has just one entry - to run a single Method - but in many cases it has several components, for example: 8 |

9 | 10 | ![Screenshot](images/screenshot4.png) 11 | 12 |
13 | #### Adding or Editing a Schema 14 | Each field is added or edited in the schema editor by specifying the field **Type** from a drop-down list... 15 |

16 | 17 | ![Screenshot](images/screenshot5.png) 18 | 19 |
20 | ...and the field **Data Type** from a drop-down list: 21 |

22 | 23 | ![Screenshot](images/screenshot6.png) 24 | 25 |
26 | We can define default values for fields in a class schema, which will be inherited by all instances created from the class, but can be optionally over-ridden in the schema of any particular instance. 27 | 28 | ### Instances 29 | 30 | An _Instance_ is a specific "clone" of the generic class, and is the entity run by the Automation Engine. An Instance contains a copy of the Class schema but with actual values of the fields filled in. 31 | 32 | ### Relationships 33 | 34 | One of the schema field types is _Relationship_, and these are links to other Instances elsewhere in the Automation Datastore. We often use relationships as a way of chaining Instances together, and relationship values can accept variable substitutions for flexibility, for example: 35 |

36 | 37 | ![Screenshot](images/screenshot7.png) 38 | -------------------------------------------------------------------------------- /chapter1/domains_and_namespaces.md: -------------------------------------------------------------------------------- 1 | ## Domains and Namespaces 2 | 3 | ### Domains 4 | A _Domain_ is a collection of Namespaces, Classes, Instances and Methods. The ManageIQ project provides a single _ManageIQ_ Domain for all supplied automation code, while Red Hat adds the supplemental _RedHat_ Domain containing added-value code for the CloudForms product. 5 |

6 | 7 | ![Screenshot](images/screenshot1.png) 8 | 9 |
10 | Both the ManageIQ and RedHat Domains are locked, indicating their read-only nature, however we can create new Domains for our own custom automation code. Organising our own code into custom Domains greatly simplifies the task of exporting and importing code (simplifying code portability and re-use). It also leaves ManageIQ or Red Hat free to update the locked Domains through minor releases without fear of overwriting our customisations. 11 | 12 | #### Domain Priority 13 | User-added Domains can be individually enabled or disabled, and all Domains can be layered in a priority order such that if code exists in the same path in multiple Domains (for example `/Infrastructure/VM/Provisioning/StateMachines/Methods`), the code in the highest priority enabled Domain will be executed. 14 |

15 | 16 | ![Screenshot](images/screenshot9.png) 17 | 18 | 19 | #### Importing / Exporting Domains 20 | Domains can be exported using _rake_ from the command line, and imported either using _rake_ or from the WebUI. (Using rake enables us to specify more import and export options). A typical rake import line is as follows: 21 | 22 | ``` 23 | bin/rake evm:automate:import YAML_FILE=Buttons.yaml IMPORT_AS=Bit63 \ 24 | SYSTEM=false ENABLED=true DOMAIN=Export PREVIEW=false 25 | ``` 26 | 27 | See the following kbase articles for details and examples of importing and exporting Domains using rake: 28 | 29 | [Cloudforms 3.1 Exporting Automate Domains](https://access.redhat.com/solutions/1225313) 30 | 31 | [Cloudforms 3.1 Importing Automate Domains](https://access.redhat.com/solutions/1225383) 32 | 33 | #### Copying Objects Between Domains 34 | 35 | We frequently need to customise code in the locked RedHat or ManageIQ Domains, for example when implementing our own custom VM Placement method. Fortunately we can easily copy any object from the locked Domains into our own, using **Configuration -> Copy this ...** 36 |

37 | 38 | ![Screenshot](images/screenshot10.png) 39 | 40 | 41 | ![Screenshot](images/screenshot3.png) 42 | 43 | #### Importing Old Format Exports 44 | 45 | Domains are a new feature of the Automation engine for the ManageIQ _Anand_ release (CloudForms Management Engine 5.3). Prior to this release all factory-supplied and user-created automation code was contained in a common structure, which made updates difficult when any user-added code was introduced (the user-supplied modifications needed exporting and re-importing/merging whenever an automation update was released). 46 | 47 | To import a Datastore backup from a 3.0 and prior format Datastore, it must be converted to the new Datastore format first, e.g. 48 | 49 | ``` 50 | cd /var/www/miq/vmdb 51 | bin/rake evm:automate:convert FILE=database.xml DOMAIN=SAMPLE \ 52 | ZIP_FILE=/tmp/sample_converted.zip 53 | ``` 54 | 55 | See also... 56 | 57 | [Cloudforms 3.1 Automate Model Conversion](https://access.redhat.com/solutions/1225413) 58 | 59 | ###Namespaces 60 | A _Namespace_ is a folder-like container for Classes, Instances and Methods, and is purely used for organisational purposes. Namespaces can contain other Namespaces, as shown in the following diagram: 61 | 62 | ![Screenshot](images/screenshot2.png) 63 | 64 | We create Namespaces to arrange our code logically. 65 | -------------------------------------------------------------------------------- /chapter1/images/datastore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/datastore.png -------------------------------------------------------------------------------- /chapter1/images/datastores_cfme_54_55.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/datastores_cfme_54_55.graffle/data.plist -------------------------------------------------------------------------------- /chapter1/images/datastores_cfme_54_55.graffle/image2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/datastores_cfme_54_55.graffle/image2.tiff -------------------------------------------------------------------------------- /chapter1/images/datastores_cfme_54_55.graffle/image3.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/datastores_cfme_54_55.graffle/image3.tiff -------------------------------------------------------------------------------- /chapter1/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot1.png -------------------------------------------------------------------------------- /chapter1/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot10.png -------------------------------------------------------------------------------- /chapter1/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot11.png -------------------------------------------------------------------------------- /chapter1/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot12.png -------------------------------------------------------------------------------- /chapter1/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot2.png -------------------------------------------------------------------------------- /chapter1/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot3.png -------------------------------------------------------------------------------- /chapter1/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot4.png -------------------------------------------------------------------------------- /chapter1/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot5.png -------------------------------------------------------------------------------- /chapter1/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot6.png -------------------------------------------------------------------------------- /chapter1/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot7.png -------------------------------------------------------------------------------- /chapter1/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot8.png -------------------------------------------------------------------------------- /chapter1/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter1/images/screenshot9.png -------------------------------------------------------------------------------- /chapter1/methods.md: -------------------------------------------------------------------------------- 1 | ## Methods 2 | 3 | ### Methods 4 | 5 | A _Method_ is a self-contained block of Ruby code. This is the code that gets executed when we run any Automation operation. 6 |

7 | 8 | ![Screenshot](images/screenshot8.png) 9 | 10 |
11 | Methods can have one of three _Location_ values: _inline_, _builtin_, or _URI_. In practice most of the methods that we create are _inline_ methods. 12 | 13 | 14 | -------------------------------------------------------------------------------- /chapter1/the_automation_datastore.md: -------------------------------------------------------------------------------- 1 | ## Introduction to the Automation Datastore 2 | 3 | Before we start investigating CloudForms/ManageIQ Automation in detail, it's worth taking a tour of the Automation Datastore, to familiarise ourselves with the objects that we'll find there. 4 | 5 | ### The Automation Datastore 6 | 7 | The Automation Datastore has a directory-like structure, consisting of several types of object. The screenshots below show the icon styles for CloudForms Management Engine 5.4 (_Botvinnik_) (left) and CloudForms Management Engine 5.5 (_Capablanca_) (right) 8 |

9 | 10 | ![Datastore](images/screenshot12.png) 11 | 12 |
13 | We can look at each of these types of object in more detail in the following sections. 14 | -------------------------------------------------------------------------------- /chapter10/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter10/images/screenshot1.png -------------------------------------------------------------------------------- /chapter10/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter10/images/screenshot2.png -------------------------------------------------------------------------------- /chapter10/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter10/images/screenshot3.png -------------------------------------------------------------------------------- /chapter10/ways_of_entering_automation.md: -------------------------------------------------------------------------------- 1 | ## Ways of Entering Automation 2 | 3 | So far we have launched Automation scripts in two ways; from **Simulate**, and from a **Custom Button**. Both of these methods can call an Automation Instance in either 4 | `/System/Request` or `/System/Event` in the Automation Datastore, and in the examples we've seen so far we've called `/System/Request/Call_Instance` and `/System/Request/InspectMe`. 5 | 6 | In fact all but one of the ways of invoking Automation must call an entry point Instance in either 7 | `/System/Request` or `/System/Event`; the exception being when calling Automation from the RESTful API, which can invoke any Instance anywhere in the Automation Datastore. 8 | 9 | There are a further two ways that an Automation script can be launched. 10 | 11 | ### Control Policy Actions 12 | 13 | A _Control Policy Action_ can be created that launches a Custom Automation Instance: 14 |

15 | 16 | ![screenshot](images/screenshot1.png?) 17 | 18 |
19 | This can launch any Instance in `/System/Request`, but as before we can use `Call_Instance` to redirect the call via the in-built **rel2** Relationship to an Instance in our own Domain and Namespace. 20 | 21 | ### Alerts 22 | 23 | With ManageIQ _Botvinnik_ (CloudForms Management Engine 5.4) and prior, an _Alert_ can be created that sends a Management Event. This calls an Instance under `/System/Event` in the Automation Datastore that corresponds to the Management Event name: 24 |

25 | 26 | ![screenshot](images/screenshot2.png) 27 | 28 |
29 | We can clone the `/System/Event` namespace to our own domain, and add the corresponding Instance: 30 |

31 | 32 | ![screenshot](images/screenshot3.png) 33 | 34 |
35 | This Instance will now be run when the Alert is triggered. 36 | 37 | ### Writing Generic Methods 38 | 39 | Our entry point into Automate governs the content of `$evm.root` - this is the object whose instantiation took us into Automate. If we write a generically useful method such as one that adds a disk to a VM, it might be useful to be able to call it in several ways, without necessarily knowing what `$evm.root` might contain. 40 | 41 | For example we might wish to add a disk during the provisioning workflow for the VM; from a button on an existing VM object in the WebUI, or even from an external RESTful call into the Automate Engine (passing the VM ID as an argument). The contents of $evm.root is different in each of these cases. 42 | 43 | For each of these cases we need to access the target VM Object in a different way, but we can use the ```$evm.root['vmdb_object_type']``` key to help us establish context: 44 | 45 | 46 | ```ruby 47 | case $evm.root['vmdb_object_type'] 48 | when 'miq_provision' # called from a VM provision workflow 49 | vm = $evm.root['miq_provision'].destination 50 | ... 51 | when 'vm' 52 | vm = $evm.root['vm'] # called from a button 53 | ... 54 | when 'automation_task' # called from a RESTful automation request 55 | attrs = $evm.root['automation_task'].options[:attrs] 56 | vm_id = attrs[:vm_id] 57 | vm = $evm.vmdb('vm').find_by_id(vm_id) 58 | ... 59 | end 60 | ``` 61 | -------------------------------------------------------------------------------- /chapter11/event_processing.md: -------------------------------------------------------------------------------- 1 | ## Event Processing 2 | 3 | One of the powerful features of CloudForms/ManageIQ is its event processing capability. An Appliance has the ability to monitor and respond to external (Provider) events, and also to raise its own events that can be handled by Automation or Control Policies. 4 | 5 | Event handling has been substantially re-written with ManageIQ _Capablanca_ (CloudForms Management Engine 5.5). The following sections examine the new Event Switchboard, and how external events are caught and internal events are raised and handled. 6 | -------------------------------------------------------------------------------- /chapter11/event_switchboard.md: -------------------------------------------------------------------------------- 1 | ## The Event Switchboard 2 | 3 | The way that CloudForms/ManageIQ handles events has changed with ManageIQ _Capablanca_. Prior to _Capablanca_ events entered Automation at `/System/Process/Event`, and were redirected to the appropriate Instance under the `/System/Event` Class based on the translation of the `#event_type` variable: 4 |

5 | 6 | ![screenshot](images/screenshot1.png) 7 | 8 | For example `#event_type` might translate to **request\_created**: 9 |

10 | 11 | ![screenshot](images/screenshot2.png) 12 | 13 | ### The Event Stream 14 | 15 | For _Capablanca_ (CloudForms Management Engine 5.5) event handling is expanded, and events are now handled as part of an _event stream_. The `/System/Process/Event` Class contains a new **rel5** Relationship that redirects the handling of the event into the _Event Switchboard_: 16 | 17 | `/System/Event/${/#event_stream.event_namespace}/${/#event_stream.source}/${/#event_type}` 18 |

19 | 20 | 21 | ![screenshot](images/screenshot8.png) 22 | 23 | ### Switchboard Components Parts 24 | 25 | The Relationship into the Switchboard is broken down into three parts, each selected from the substitution of a run-time variable. 26 | 27 | #### Event Stream Namespace 28 | 29 | The `${/#event_stream.event_namespace}` part of the Relationship translates to one of three _Event Stream Namespaces_: `EmsEvent` if the event's origin was from an _External Management System_ (i.e. a Provider); `MiqEvent` if the event's origin was an internal CloudForms/ManageIQ-initiated _Policy_ event; or `RequestEvent` if the event is related to an Automation Request (e.g. **request\_created**): 30 |

31 | 32 | ![screenshot](images/screenshot10.png) 33 | 34 | #### Event Stream Source 35 | 36 | Within each of the Event Stream Namespaces, are Classes that define the _Event Stream Source_ Instances. The selection of source Class is made from the substitution of the `${/#event_stream.source}` part of the `/System/Process/Event` **rel5** Relationship. We can see that for the `EmsEvent` Namespace, these represent the various _External Management Systems_ (**Amazon**, **OpenStack**, etc.): 37 |

38 | 39 | ![screenshot](images/screenshot5.png) 40 | 41 | #### Event Type 42 | 43 | Under the apprpriate Event Stream Source Classes are Instances that define the processing required for each _Event Type_. The selection of Event Type is made from the substitution of the `${/#event_type}` part of the `/System/Process/Event` **rel5** Relationship. We can see that these represent the various events that the **EventCatcher::Runner** workers detect from the Provider message bus, for example for the `Amazon` Namespace: 44 |

45 | 46 | ![screenshot](images/screenshot6.png) 47 | 48 | The Event Type Instances contain one or more Relationships to _Event Handlers_ in the `/System/event_handlers` Namespace that define what actions to take for that event. For example the **Amazon** event **AWS\_EC2\_Instance\_running** will call the `event_action_policy` handler to push a new **vm\_start** policy event through the Switchboard. It also calls the `event_action_refresh` handler to trigger a Provider refresh so that the current Instance details can be retrieved: 49 |

50 | 51 | ![screenshot](images/screenshot9.png?) 52 | 53 | #### Event Handlers 54 | The Event Handlers are Instances and Methods that perform the actual granular processing for each event. The Methods are _builtin_ for execution efficiency; their code is not visible in the Automate Explorer. 55 |

56 | 57 | ![screenshot](images/screenshot11.png) -------------------------------------------------------------------------------- /chapter11/extending_event_handling.md: -------------------------------------------------------------------------------- 1 | ## Extending Automate Event Handling 2 | 3 | The Provider-specific Event Stream Source Classes and associated Instances under `/System/Event/EmsEvent` do not necessarily handle every possible event that can be raised by the Provider. Sometimes we need to extend event handling to process a non-default event. 4 | 5 | We can extend the out-of-the-box event handling by creating our own Instances under `/System/Event` (ManageIQ _Botvinnik_) or `/System/Event/EmsEvent/{Provider}` (ManageIQ _Capablanca_) to handle these non-default events caught by the EventCatcher workers. 6 | 7 | As an example the _compute.instance.power\_on.end_ OpenStack event is not handled by default with ManageIQ _Capablanca_. If we look in `evm.log` we see: 8 | 9 | ``` 10 | Instance [/ManageIQ/System/Event/EmsEvent/OPENSTACK/compute.instance.power_on.end] \ 11 | not found in MiqAeDatastore - trying [.missing] 12 | ``` 13 | 14 | As a result, the Cloud Instance's tile quadrant in the WebUI that shows power status doesn't change to reflect the Instance being powered on. 15 | 16 | ### Adding a New Automation Instance to /System/Event/EmsEvent/ 17 | 18 | There is already a `ManageIQ/System/Event/EmsEvent/OpenStack/compute.instance.power_off.end` Instance to handle the _compute.instance.power\_off.end_ event. This Instance calls two event\_handlers: 19 | 20 | ![screenshot](images/screenshot19.png) 21 | 22 | 23 | We can copy this Instance to our Domain and rename it as `/System/Event/EmsEvent/OpenStack/compute.instance.power_on.end`. 24 |

25 | 26 | ![screenshot](images/screenshot18.png) 27 | 28 | We change the second event\_handler line to trigger a **vm_start** policy event: 29 | 30 | ![screenshot](images/screenshot20.png) 31 | 32 | Now when we power on an OpenStack Intance, we see the Instance's tile quadrant change correctly, and we see the raising and processing of the **vm_start** event: 33 | 34 | ``` 35 | Instantiating [/System/Process/Event? \ 36 | EventStream%3A%3Aevent_stream=1000000009501&MiqEvent%3A%3Amiq_event=1000000009501& \ 37 | MiqServer%3A%3Amiq_server=1000000000001& \ 38 | User%3A%3Auser=1000000000001& \ 39 | VmOrTemplate%3A%3Avm=1000000000035& \ 40 | ems_event=1000000009500& \ 41 | event_stream_id=1000000009501& \ 42 | event_type=vm_start& \ 43 | ext_management_systems=1000000000002& \ 44 | manageiq%3A%3Aproviders%3A%3Aopenstack%3A%3Acloudmanager%3A%3Avm=1000000000035& \ 45 | miq_event_id=1000000009501& \ 46 | object_name=Event& \ 47 | vmdb_object_type=vm] 48 | ``` 49 | 50 | This will ensure that any Control Policies that are triggered by a **VM Power On** Event will run correctly. -------------------------------------------------------------------------------- /chapter11/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot1.png -------------------------------------------------------------------------------- /chapter11/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot10.png -------------------------------------------------------------------------------- /chapter11/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot11.png -------------------------------------------------------------------------------- /chapter11/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot12.png -------------------------------------------------------------------------------- /chapter11/images/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot13.png -------------------------------------------------------------------------------- /chapter11/images/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot14.png -------------------------------------------------------------------------------- /chapter11/images/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot15.png -------------------------------------------------------------------------------- /chapter11/images/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot16.png -------------------------------------------------------------------------------- /chapter11/images/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot17.png -------------------------------------------------------------------------------- /chapter11/images/screenshot18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot18.png -------------------------------------------------------------------------------- /chapter11/images/screenshot19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot19.png -------------------------------------------------------------------------------- /chapter11/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot2.png -------------------------------------------------------------------------------- /chapter11/images/screenshot20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot20.png -------------------------------------------------------------------------------- /chapter11/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot3.png -------------------------------------------------------------------------------- /chapter11/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot4.png -------------------------------------------------------------------------------- /chapter11/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot5.png -------------------------------------------------------------------------------- /chapter11/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot6.png -------------------------------------------------------------------------------- /chapter11/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot7.png -------------------------------------------------------------------------------- /chapter11/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot8.png -------------------------------------------------------------------------------- /chapter11/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter11/images/screenshot9.png -------------------------------------------------------------------------------- /chapter12/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter12/images/screenshot1.png -------------------------------------------------------------------------------- /chapter12/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter12/images/screenshot2.png -------------------------------------------------------------------------------- /chapter12/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter12/images/screenshot3.png -------------------------------------------------------------------------------- /chapter12/images/state_machine_logic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter12/images/state_machine_logic.png -------------------------------------------------------------------------------- /chapter13/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot1.png -------------------------------------------------------------------------------- /chapter13/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot2.png -------------------------------------------------------------------------------- /chapter13/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot3.png -------------------------------------------------------------------------------- /chapter13/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot4.png -------------------------------------------------------------------------------- /chapter13/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot5.png -------------------------------------------------------------------------------- /chapter13/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter13/images/screenshot6.png -------------------------------------------------------------------------------- /chapter13/more_advanced_schema_concepts.md: -------------------------------------------------------------------------------- 1 | ## More Advanced Class and Schema Concepts 2 | There are three more Class/Schema concepts that we use with varying degrees of frequency. 3 | 4 | ### Messages 5 | Each Schema Field has a **Message** column/value that we can optionally use to identify a particular Field to execute or evaluate when we call the Instance. We can think of this as an index key into the schema. 6 | 7 | The default message is _create_, and if we look at the schema that we created for our `/ACME/General/Methods` class, we see that the default Message value of _create_ was automatically set for us for all fields: 8 |

9 | 10 | ![screenshot](images/screenshot3.png) 11 | 12 |
13 | We specify the Message when we create a Relationship to an Instance, by appending `#message` after the URI to the Instance. If we don't explicitly specify a Message then `#create` is implicitly used. 14 | 15 | For example we could create a Relationship to run our first _HelloWorld_ Instance, using a URI of either: 16 | 17 | ``` 18 | /ACME/General/Methods/HelloWorld 19 | ``` 20 | 21 | or 22 | 23 | ``` 24 | /ACME/General/Methods/HelloWorld#create 25 | ``` 26 | 27 | In both cases the `hello_world` Method would execute as this is the _Method_ schema field "indexed" by the _create_ message. 28 | 29 | #### Specifying our own Messages 30 | It can be useful to create a Class/Instance schema that allows for one of several Methods to be executed, depending on the Message passed to the Instance at run-time. For example the Schema for the `Infrastructure/VM/Provisioning/Placement` Class allows for a Provider-specific VM placement algorithm to be created: 31 |

32 | 33 | ![screenshot](images/screenshot2.png) 34 | 35 |
36 | The `default` Instance created from this Class has the Method values filled in accordingly: 37 |

38 | 39 | ![screenshot](images/screenshot4.png) 40 | 41 |
42 | This means that it can be called as part of the VM Provisioning State Machine, by appending a Message created from a variable substitution corresponding to the provisioning source vendor (i.e. redhat, vmware or microsoft): 43 | 44 | ``` 45 | /Infrastructure/VM/Provisioning/Placement/default#${/#miq_provision.source.vendor} 46 | ``` 47 | ![screenshot](images/screenshot5.png) 48 |

49 | In this way we are able to create a generic Class and Instance definition that contains several Methods, and the choice of which Method to run can be selected dynamically at run-time by using a Message. 50 | 51 | ### Assertions 52 | An _Assertion_ is a boolean check that we can put in our Class schema. Assertions are always processed first, regardless of their position on the schema. If an Assertion evaluates to _true_ the remaining Instance schema fields are processed, (i.e. the Instance continues to run). If an Assertion evaluates to _false_ the remainder of the Instance fields are not processed, and the Instance stops. 53 | 54 | An example of an Assertion is found at the start of the Schema for the `Placement` Class. Placement methods are only relevant if the **Automatic** check box has been selected at provisioning time, and this check box sets a boolean value `miq_provision.placement_auto`. The Assertion checks that this value is true, and prevents the remainder of the Instance from running if automatic placement has not been selected. 55 | 56 | ![screenshot](images/screenshot1.png) 57 |

58 | 59 | ### Collect 60 | As we have seen, there is a parent - child relationship between the `$evm.root` object (the one whose instantiation took us into the Automation Engine), and subsequent objects created as a result of following schema relationships or by calling `$evm.instantiate`. 61 | 62 | If a child object has Achema Attribute values, it can read or write to them by using its own `$evm.object` hash (e.g. we saw the use of `$evm.object['username']` in [Using Schema Object Variables](../chapter3/using_schema_object_variables.md)). Sometimes we need to propagate these values back up the parent `$evm.root` object, and we do this using _Collections_. 63 | 64 | We define a value to collect in the **Collect** Schema column, using the syntax ```/root_variable_name = schema_variable_name```, e.g. 65 |

66 | 67 | ![screenshot](images/screenshot6.png) 68 | 69 |
70 | 71 | In this example Schema, the child object has three Schema attributes defined, **pre\_dialog\_name**, **dialog_name**, and **state_machine**. 72 | 73 | If a local Method were to reference these it would use the syntax `$evm.object['pre_dialog_name']`, `$evm.object['dialog_name']` or `$evm.object['state_machine']`, however the **Collect** value also makes these same Attribute values available to the root object as `$evm.root['dialog_name']` and `$evm.root['state_machine']`. 74 | 75 | Collections make it possible for root objects to spawn "worker" child Instances to discover various Attribute values, and to have these values propagated back to the root object to be used in "bigger picture" coordination and orchestration tasks. 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /chapter14/automation_operations.md: -------------------------------------------------------------------------------- 1 | ## Automation Operations 2 | 3 | It can be useful to examine in detail some of the operational functionality of the Automation Engine. 4 | 5 | The next sections examine how some Automation operations are separated into Requests and Tasks, and how the Automation functionality of CloudForms/ManageIQ has been written for distributed scalability. -------------------------------------------------------------------------------- /chapter15/approval.md: -------------------------------------------------------------------------------- 1 | ## Approval 2 | 3 | The approval process for a VM Provision Request is entered as a result of the `/System/Policy/MiqProvisionRequest_created` policy being run from a _request\_created_ event. This results in a VM Provisioning Profile lookup to read the value of the **auto\_approval\_state\_machine** attribute, which by default is `ProvisionRequestApproval` for an Infrastructure VM Provision Request. The second relationship from the event runs the `Default` Instance of this State Machine. 4 |

5 | 6 | ![screenshot](images/screenshot33.png) 7 | 8 |
9 | The Schema for the `ProvisionRequestApproval` State Machine is: 10 |

11 | 12 | ![screenshot](images/screenshot34.png) 13 | 14 |
15 | The `Default` Instance has the following Field values: 16 |

17 | 18 | ![screenshot](images/screenshot35.png) 19 |
20 | 21 | This Instance will auto-approve any VM Provisioning Request containing a single VM, but requests for more than this number will require explicit approval from an Administrator, or anyone in a Group with the role **EvmRole-approver** (or equivalent). 22 | 23 | We can copy the `Default` Instance (including path) to our own Domain and change or set any of the auto-approval schema Attributes, i.e. **max_cpus**, **max_vms**, **max_memory** or **max\_retirement\_days**. 24 | 25 | ### Overriding the Schema Default - Template Tagging 26 | 27 | We can override the auto-approval **max_*** values stored in the `ProvisionRequestApproval` State Machine on a per-Template basis, by applying tags from one or more of the following tag categories to the Template: 28 |
29 | 30 | | Tag Category Name | Tag Category Display Name | 31 | |:----------:|:----------------:| 32 | | prov\_max\_cpu | Auto Approve - Max CPU | 33 | | prov\_max\_memory | Auto Approve - Max Memory | 34 | | prov\_max\_retirement\_days | Auto Approve - Max Retirement Days | 35 | | prov\_max\_vm | Auto Approve - Max VM | 36 |
37 | 38 | If a Template is tagged in such a way, then any VM Provisioning Request _from_ that Template will result in the Template's tag value being used for auto-approval considerations, rather than the Attribute value from the schema. 39 | 40 | ### VM Provisioning-Related Email 41 | 42 | There are four Email Instances with corresponding Methods that are used to handle the sending of VM Provisioning-related emails. The Instances each have the Attributes **to\_email\_address**, **from\_email\_address** and **signature** that can (and should) be customised, after copying the Instances to our own Domain. 43 | 44 | Three of the Instances are approval-related. The **to\_email\_address** value for the `MiqProvisionRequest_Pending` Instance should contain the email address of a user (or mailing list) who is able to login to the CloudForms appliance as an Administrator or as a member of a Group with the role **EvmRole-approver** (or equivalent). 45 |

46 | 47 | ![screenshot](images/screenshot36.png?) 48 |
49 | 50 | -------------------------------------------------------------------------------- /chapter15/images/provisioning_objects.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/provisioning_objects.graffle -------------------------------------------------------------------------------- /chapter15/images/provisioning_objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/provisioning_objects.png -------------------------------------------------------------------------------- /chapter15/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot1.png -------------------------------------------------------------------------------- /chapter15/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot10.png -------------------------------------------------------------------------------- /chapter15/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot11.png -------------------------------------------------------------------------------- /chapter15/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot12.png -------------------------------------------------------------------------------- /chapter15/images/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot13.png -------------------------------------------------------------------------------- /chapter15/images/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot14.png -------------------------------------------------------------------------------- /chapter15/images/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot15.png -------------------------------------------------------------------------------- /chapter15/images/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot16.png -------------------------------------------------------------------------------- /chapter15/images/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot17.png -------------------------------------------------------------------------------- /chapter15/images/screenshot18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot18.png -------------------------------------------------------------------------------- /chapter15/images/screenshot19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot19.png -------------------------------------------------------------------------------- /chapter15/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot2.png -------------------------------------------------------------------------------- /chapter15/images/screenshot20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot20.png -------------------------------------------------------------------------------- /chapter15/images/screenshot21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot21.png -------------------------------------------------------------------------------- /chapter15/images/screenshot22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot22.png -------------------------------------------------------------------------------- /chapter15/images/screenshot23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot23.png -------------------------------------------------------------------------------- /chapter15/images/screenshot24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot24.png -------------------------------------------------------------------------------- /chapter15/images/screenshot25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot25.png -------------------------------------------------------------------------------- /chapter15/images/screenshot26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot26.png -------------------------------------------------------------------------------- /chapter15/images/screenshot27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot27.png -------------------------------------------------------------------------------- /chapter15/images/screenshot28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot28.png -------------------------------------------------------------------------------- /chapter15/images/screenshot29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot29.png -------------------------------------------------------------------------------- /chapter15/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot3.png -------------------------------------------------------------------------------- /chapter15/images/screenshot30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot30.png -------------------------------------------------------------------------------- /chapter15/images/screenshot31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot31.png -------------------------------------------------------------------------------- /chapter15/images/screenshot32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot32.png -------------------------------------------------------------------------------- /chapter15/images/screenshot33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot33.png -------------------------------------------------------------------------------- /chapter15/images/screenshot34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot34.png -------------------------------------------------------------------------------- /chapter15/images/screenshot35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot35.png -------------------------------------------------------------------------------- /chapter15/images/screenshot36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot36.png -------------------------------------------------------------------------------- /chapter15/images/screenshot37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot37.png -------------------------------------------------------------------------------- /chapter15/images/screenshot38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot38.png -------------------------------------------------------------------------------- /chapter15/images/screenshot39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot39.png -------------------------------------------------------------------------------- /chapter15/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot4.png -------------------------------------------------------------------------------- /chapter15/images/screenshot40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot40.png -------------------------------------------------------------------------------- /chapter15/images/screenshot41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot41.png -------------------------------------------------------------------------------- /chapter15/images/screenshot42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot42.png -------------------------------------------------------------------------------- /chapter15/images/screenshot43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot43.png -------------------------------------------------------------------------------- /chapter15/images/screenshot44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot44.png -------------------------------------------------------------------------------- /chapter15/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot5.png -------------------------------------------------------------------------------- /chapter15/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot6.png -------------------------------------------------------------------------------- /chapter15/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot7.png -------------------------------------------------------------------------------- /chapter15/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot8.png -------------------------------------------------------------------------------- /chapter15/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter15/images/screenshot9.png -------------------------------------------------------------------------------- /chapter15/placement.md: -------------------------------------------------------------------------------- 1 | ## VM Placement During Provisioning 2 | 3 | One of the stages in the `VMProvision_VM` State Machine is **Placement**, and it's here that the decision is made where to create the new VM, i.e. which Cluster, Host and Datastore. 4 | 5 | The value for the **Placement** stage in the `template` Instance of this State Machine is: 6 |
7 | 8 | ```ruby 9 | /Infrastructure/VM/Provisioning/Placement/default#${/#miq_provision.source.vendor} 10 | ``` 11 |
12 | ![screenshot](images/screenshot24.png) 13 |
14 | 15 | The `default` Instance is as follows: 16 | 17 |
18 | ![screenshot](images/screenshot25.png) 19 |
20 | 21 | We see that the Provider-appropriate best-fit method is selected by a message. The `redhat_best_fit_cluster` Method just places the new VM into the same cluster as the source template. The other two Methods select the Host with the least running VMs, and most available Datastore space. 22 | 23 | ### Customising Placement 24 | 25 | As part of the added-value that CloudForms brings over ManageIQ, the `RedHat` Domain includes improved placement Methods that we can optionally use: 26 | 27 |
28 | ![screenshot](images/screenshot26.png) 29 |
30 | 31 | The `*_with_scope` Methods allow us to apply a tag from the **prov\_scope** (Provisioning Scope) tag category to selected Hosts and Datastores. This tag indicates whether or not they should be included for consideration for automatic VM placement. The **prov\_scope** tag should be "all", or the name of an Access Control User Group. By tagging with a group name, we can direct selected workloads (such as developer VMs) to specific Hosts and Datastores. 32 | 33 | The `vmware_best_fit_with_tags` Method considers any Host or Datastore tagged with the same tag as the provisioning request, i.e. selected from the **Purpose** tab of the Provisioning Dialog. 34 | 35 | All three `RedHat` Domain Methods also allow us to set thresholds for Datastore usage in terms of utilisation percentage, and number of exiting VMs, when considering Datastores for placement. 36 | 37 | #### Using Alternative Placement Methods 38 | 39 | To use the `RedHat` Domain placement Methods (or any others that we choose to write), we copy the `ManageIQ/Infrastructure/VM/Provisioning/Placement/default` Instance into our own Domain, and edit the value for the **redhat**, **vmware**, or **microsoft** schema fields as appropriate to specify the name of our preferred Method. 40 | 41 |
42 | ![screenshot](images/screenshot27.png) 43 |
44 | -------------------------------------------------------------------------------- /chapter15/profile_processing.md: -------------------------------------------------------------------------------- 1 | ## Profile Processing (Deep Dive) 2 | 3 | ### The Provisioning Dialog 4 | 5 | The first Profile query is performed as soon as the requesting user selects a Template to provision from, and clicks the **Continue** button. The WebUI must launch the correct Provisioning Dialog for the target platform, operation type, and (optionally) the User Group, and it determines this information from the Profile (the Provisioning Dialog presents the main set of tabs and elements to prompt for all the information that we need to provision the VM, i.e. VM name, number of CPUs, VLAN, etc.) 6 |

7 | 8 | ![screenshot](images/screenshot8.png) 9 | 10 | 11 |
12 | The Profile is queried using the messages **get\_pre\_dialog\_name** and **get\_dialog\_name** by an internal (non-Automate) method `miq_request_workflow.rb` when we select a template and click the **Continue** button. It queries the **pre\_dialog\_name** and **dialog\_name** Attributes, and runs the `vm_dialog_name_prefix` Method: 13 | 14 | ``` 15 | ...Querying Automate Profile for dialog name 16 | ...Invoking [inline] method [.../Profile/vm_dialog_name_prefix] with inputs [{}] 17 | ...vm_dialog_name_prefix> Detected Platform: 18 | ...vm_dialog_name_prefix> Platform: dialog_name_prefix: 19 | ... 20 | ...Loading dialogs for user 21 | ``` 22 | 23 | ### VM Name (Pass 1) 24 | 25 | The Profile is queried using the message **get\_vmname** to retrieve the Instance URI to be used to formulate the name of the VM to be provisioned. This VM name is then inserted into the text string that will form the Request Object's `.description` attribute (`miq_provision_request.description`), e,g, "Provision from [rhel7-generic] to [rhel7srv004]". 26 | 27 | If we are provisioning two or more VMs in a single request and letting Automate handle the VM auto number incrementing (e.g. rhel7srv005, rhel7srv006... etc) then the Request Object description is more generic, e.g. "Provision from [rhel7-generic] to [rhel7srvxxx]". 28 | 29 | ### Approval 30 | 31 | Once the _Request_ object is created, we begin a series of Event-driven processing steps based on Instances in `/System/Policy` 32 |

33 | ![screenshot](images/screenshot9.png) 34 | 35 |
36 | The first of these to be triggered is `MiqProvisionRequest_created`. This contains two Relationships, the first of which queries the Profile using the message **get\_auto\_approval\_state\_machine** to retrieve the State Machine name to be used to handle the auto-approval process. The second relationship runs the `Default` Instance of this State Machine. 37 | 38 | #### Approved, Pending or Denied 39 | 40 | Depending on the outcome of the approval process (approved, pending or denied), an email is sent to the requester by the corresponding Event/Policy Instance. 41 | 42 | ### Quota 43 | The next Event-driven Policy Instance to be triggered is `MiqProvisionRequest_starting`. On ManageIQ _Botvinnik_ (CloudForms Management Engine 5.4) this contains two Relationships. The first of these queries the Profile using the message **get\_quota\_state\_machine** to retrieve the State Machine name to be used to handle the quota-checking process. The second relationship runs the `Default` Instance of this State Machine. 44 | 45 | Quota handling has been re-written for ManageIQ _Capablanca_ (CloudForms Management Engine 5.5), and so the `MiqProvisionRequest_starting` Policy Instance just contains a single Relationship to the `/System/CommonMethods/QuotaStateMchine/quota` State Machine. 46 | 47 | Once quota has been checked and passed, the _Request_ continues processing, and the _Task_ objects are created. 48 | 49 | ### VM Name (Pass 2) 50 | 51 | The Profile is again queried using the message **get\_vmname** to retrieve the Instance URI to be used to formulate the name of the VM to be provisioned. This second call is made while processing the Provisioning _Request_ as part of the creation of the _Tasks_ that will handle the provisioning of each VM in the _Request_. The derived VM name is added to the _Task_ object's options hash as `miq_provision.options[:vm_target_name]` and `miq_provision.options[:vm_target_hostname]`. This is performed once per Task Object (there may be several Task Objects created for a single Request Object). 52 | 53 | ### VM Provisioning State Machine 54 | 55 | Finally the Profile is used by the provisioning _Task_ to determine the State Machine to be used to provision the VM. A call is made to ```/Infrastructure/VM/Lifecycle/Provisioning#create``` 56 | 57 |
58 | ![screenshot](images/screenshot10.png) 59 | 60 |
61 | This Instance contains two Relationships, the first of which queries the Profile using the message **get\_state\_machine** to retrieve the State Machine name to be used to handle the provisioning of the VM. The second relationship runs the Instance of this State Machine whose name corresponds to a variable substitution for `miq_provision.provision_type`. When performing a VM clone from template (the most common VM provision operation), this will be `template`. 62 | -------------------------------------------------------------------------------- /chapter15/provisioning_profile.md: -------------------------------------------------------------------------------- 1 | ## The Provisioning Profile 2 | 3 | Provisioning Profiles store the attributes, relationships and methods that are used to determine the User Group-specific operations and decisions that must be made whenever a VM is provisioned. These include the selection of the appropriate Provisioning Dialog, checking the provisioning request against user or group quotas, the triggering of an approval workflow for large VM requests, and the option to use group-specific VM naming and network allocation Methods. 4 | 5 | ### Location 6 | 7 | The Provisioning Profiles are stored under `/Infrastructure/VM/Provisioning/Profile`. There are two out-of-the-box group-specific profiles for the **EvmGroup-super\_administrator** and **EvmGroup-user\_self\_service** groups, but we can create new profiles for any user groups that we wish to provision from. If a user who is not a member of a listed Group Profile provisions a VM, the `.missing` Profile will be used. 8 | 9 | ![screenshot](images/screenshot4.png) 10 | 11 | ###Schema 12 | 13 | The Provisioning Profile schema contains a number of Attributes, Relationships and Methods, as shown: 14 |
15 | 16 | ![screenshot](images/screenshot1.png?) 17 | 18 |
19 | Each of these is selected using a _Message_, and the Attributes are propagated up to `$evm.root` in the Provisioning operation using _Collect_, as follows: 20 |

21 | 22 | ![screenshot](images/screenshot5.png) 23 | 24 | ### Customising the Profile 25 | 26 | The Profile is designed to be user-customisable, and in fact we frequently add Profiles for specific user groups, or edit the `.missing` profile to cater for updated VM Naming methods, or modified Provisioning Dialogs. 27 | 28 | -------------------------------------------------------------------------------- /chapter15/quota_management.md: -------------------------------------------------------------------------------- 1 | ## Quota Management 2 | 3 | Quota management has been completely re-written for ManageIQ _Capablanca_ (CloudForms Management Engine 5.5). Prior to this release, quota management for Cloud Instance, Infrastructure VM, and Service provisioning was handled under the respective `/Cloud`, `/Infrastructure`, and `/Service` Namespaces , but in _Capablanca_, quota handling has been consolidated under `/System/CommonMethods`: 4 | 5 | ![screenshot](images/screenshot37.png) 6 | 7 | The `ManageIQ/System/CommonMethods/QuotaStateMachine/quota` State Machine Instance has the following Field values: 8 |

9 | ![screenshot](images/screenshot38.png) 10 |
11 | 12 | We can see that the processing of quota follows a simple workflow of: 13 | 14 | 1. Determine the quota source 15 | 2. Determine the quota limits assigned to that source 16 | 3. Determine the resources currently used by that source 17 | 4. Determine the new resources requested by the source 18 | 5. Validate whether the new requested amount would exceed quota 19 | 20 | ### Quota Source 21 | 22 | A new concept with the re-implemented quota management mechanism is that of _quota source_. This is the entity to which the quota is applied, and by default is a Tenant. Tenant quotas can be edited in the WebUI under **Configure -> Configuration -> Access Control -> Tenants -> _tenant_**: 23 | 24 | ![screenshot](images/screenshot40.png) 25 | 26 | The tenant object keeps track of allocated values in virtual columns: 27 | 28 | ``` 29 | --- virtual columns follow --- 30 | $evm.root['tenant'].allocated_memory = 48318382080 (type: Fixnum) 31 | $evm.root['tenant'].allocated_storage = 498216206336 (type: Fixnum) 32 | $evm.root['tenant'].allocated_vcpu = 23 (type: Fixnum) 33 | $evm.root['tenant'].provisioned_storage = 546534588416 (type: Fixnum) 34 | ``` 35 | 36 | #### Alternative Quota Sources 37 | 38 | If we wish to use an alternative quota source, we can copy the `quota_source` Method to our own domain, and edit it to define `$evm.root['quota_source']` and `$evm.root['quota_source_type']` as required. A commented-out example shows how to define a group as the quota source, in which case quota handling is handled in the pre-_Capablanca_ way: 39 | 40 | 41 | ```ruby 42 | # Sample code to enable group as the default quota source. 43 | $evm.root['quota_source'] = @miq_request.requester.current_group 44 | $evm.root['quota_source_type'] = 'group’ 45 | ``` 46 | 47 | When we use an alternative quota source, we can set quota in two ways. 48 | 49 | ##### Defining Quota in the State Machine Schema (the Model) 50 | 51 | We can set generic warn and max values for each of **VM Count**, **Storage**, **CPU** and **Memory**, by copying the `ManageIQ/System/CommonMethods/QuotaStateMachine/quota` State Machine Instance into our domain, and editing the schema attributes: 52 | 53 | ![screenshot](images/screenshot41.png) 54 | 55 | Quotas defined in the Model in this way apply to all instances of the quota source (e.g. all Groups) 56 | 57 | ##### Defining Quota Using Tags 58 | 59 | We can override the default Model attributes by applying tags from one or more of the following tag categories to individual quota source entities (e.g. individual Groups): 60 |
61 | 62 | | Tag Category Name | Tag Category Display Name | Pre-Exists | 63 | |:----------:|:----------------:|:--------------:| 64 | | quota\_warn\_vms | Quota - Warn VMs | No - must be created | 65 | | quota\_max\_vms | Quota - Max VMs | No - must be created | 66 | | quota\_warn\_storage | Quota - Warn Storage | No - must be created | 67 | | quota\_max\_storage | Quota - Max Storage | Yes | 68 | | quota\_warn\_cpu | Quota - Warn CPUs | No - must be created | 69 | | quota\_max\_cpu | Quota - Max CPUs | Yes | 70 | | quota\_warn\_memory | Quota - Warn Memory | No - must be created | 71 | | quota\_max\_memory | Quota - Max Memory | Yes | 72 | 73 | 74 | For example: 75 |

76 | ![screenshot](images/screenshot42.png) 77 |

78 | If a Group is tagged in such a way, then any VM or Service Provisioning Request from any Group member is matched against the currently allocated CPUs, memory or storage for the Group. 79 | 80 | If quotas are defined in both the Model and using tags, the tagged value takes priority. 81 | 82 | ### Quota Checking During VM Provisioning 83 | 84 | The quota checking process for a VM Provision Request is entered as a result of the `/System/Policy/MiqProvisionRequest_starting` policy being run from a _request\_starting_ event. The policy Instance jumps straight to the `quota` State Machine from its **rel5** Relationship: 85 |

86 | 87 | ![screenshot](images/screenshot43.png) 88 | 89 | If the Provisioning Request would result in the quota being exceeded, then the Request is rejected, and the requesting User is emailed using the `/{Infrastructure,Cloud}/VM/Provisioning/Email/MiqProvisionRequest_Denied`email class. -------------------------------------------------------------------------------- /chapter15/scripts/start_vm.rb: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------- 2 | # 3 | # CFME Automate Method: start_vm 4 | # 5 | # Author: Peter McGowan (Red Hat) 6 | # 7 | #---------------------------------------------------------------- 8 | begin 9 | vm = $evm.root['miq_provision'].destination 10 | $evm.log(:info, "Current VM power state = #{vm.power_state}") 11 | unless vm.power_state == 'on' 12 | vm.start 13 | vm.refresh 14 | $evm.root['ae_result'] = 'retry' 15 | $evm.root['ae_retry_interval'] = '30.seconds' 16 | else 17 | $evm.root['ae_result'] = 'ok' 18 | end 19 | 20 | rescue => err 21 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 22 | $evm.root['ae_result'] = 'error' 23 | end -------------------------------------------------------------------------------- /chapter15/state_machine.md: -------------------------------------------------------------------------------- 1 | ## The VM Provisioning State Machine 2 | 3 | The VM Provisioning State Machine (`VMProvision_VM`) schema contains a number of States, as shown (illustrated is the `template` Instance of this State Machine): 4 |
5 | 6 | ![screenshot](images/screenshot11.png) 7 | 8 |
9 | Several of these States (such as **RegisterCMDB** or **RegisterAD**) contain no out-of-the-box values, but are there as placeholders should we wish to add the functionality to our own customised Instance. 10 | 11 | Some States (such as **Placement**) have values ending in a variable substitution, such as: 12 | 13 | ``` 14 | ...StateMachines/Methods/PreProvision#${/#miq_provision.source.vendor} 15 | ``` 16 | 17 | This variable defines the message to be sent to the Instance handling that State, and allows us to dynamically select Provider-specific processing options (in this case allowing for alternative pre-provisioning options for VMware and RHEV, with Microsoft SCVMM added in CloudForms Management Engine 5.5): 18 |

19 | ![screenshot](images/screenshot20.png) 20 | 21 |
22 | We can copy the State Machine into our own Domain, and add Instance URIs to any of the blank the States as required, or extend the State Machine by inserting new States. A common addition is to add a method at the **AcquireIPAddress** step to retrieve an IP address from a corporate IPAM solution such as an Infoblox Appliance. Once retrieved, the IP address is inserted into the _Task's_ options hash using the `.set_option` method, e.g. 23 |

24 | 25 | ```ruby 26 | $evm.root['miq_provision'].set_option(:ip_addr, allocated_ip_address) 27 | ``` 28 | -------------------------------------------------------------------------------- /chapter15/vm_provisioning.md: -------------------------------------------------------------------------------- 1 | ## Provisioning a VM 2 | 3 | Possibly the most complex operation that is performed by the out-of-the-box Automation Engine is that of provisioning a VM. The process is, however, designed to be extremely flexible, and allows a great deal of customisation based on tagging, the requesting user's group membership, and the destination Provider type (e.g. RHEV, VMware, OpenStack, etc.). It uses all of the Automation features that we've discussed so far in the book. 4 | 5 | ### The Provisioning Process 6 | 7 | The VM Provisioning Process starts with a user (the _requester_) selecting **Provision VMs** from under the **Infrastructure -> Virtual Machines -> Lifecycle** button group: 8 |

9 | 10 | ![screenshot](images/screenshot6.png) 11 |
12 | 13 | This takes us into a selection dialog where we pick a Template to provision from, and click the **Continue** button: 14 |

15 | 16 | ![screenshot](images/screenshot7.png) 17 | 18 | Once we click **Continue**, we enter into the VM Provisioning workflow, starting with information retrieved from the _Profile_ and moving into the _State Machine_. 19 | 20 | ### The Provisioning Profile and the VM Provisioning State Machine 21 | 22 | Provisioning a VM involves many separate steps and decisions. Some of these need to be performed or evaluated in the context of the requesting user's group membership (such as quota evaluation, or custom VM naming), while others are more generic to all VM provisioning operations. The Provisioning/Group Profile contains the group-specific Attributes, Instance and State Machine names that are used in processing the provisioning _Request_, and in preparing the provisioning _Task(s)_. 23 | 24 | The VM Provisioning State Machine is processed in the context of the provisioning _Task_, and contains the sequence of steps involved in actually provisioning the VM. 25 | -------------------------------------------------------------------------------- /chapter16/ansible_script.yml: -------------------------------------------------------------------------------- 1 | # ssh-keygen -R 192.168.1.163 first 2 | 3 | --- 4 | - hosts: 192.168.1.163 5 | vars: 6 | - satellite_host: satellite01.bit63.net 7 | - organization: Bit63 8 | - activation_key: RHEL6-Generic 9 | tasks: 10 | - name: Install Cert 11 | command: /usr/bin/yum -y localinstall http://{{ satellite_host }}/pub/katello-ca-consumer-latest.noarch.rpm 12 | 13 | - name: Register with Satellite 14 | command: /usr/sbin/subscription-manager register --org {{ organization }} --activationkey {{ activation_key }} 15 | register: registered 16 | 17 | - name: Enable Repositories 18 | command: subscription-manager repos --enable=rhel-*-satellite-tools-*-rpms 19 | when: registered|success 20 | 21 | - name: Install Katello Agent 22 | yum: pkg=katello-agent state=latest 23 | when: registered|success 24 | notify: 25 | - Enable Katello Agent 26 | - Start Katello Agent 27 | 28 | - name: Install Puppet 29 | yum: pkg=puppet state=latest 30 | when: registered|success 31 | register: puppet_installed 32 | notify: 33 | - Enable Puppet 34 | 35 | - name: Configure Puppet Agent 36 | command: /usr/bin/puppet config set server {{ satellite_host }} --section agent 37 | when: puppet_installed|success 38 | 39 | - name: Run Puppet Test 40 | command: /usr/bin/puppet agent --test --noop --onetime --waitforcert 60 41 | when: puppet_installed|success 42 | 43 | - name: Start Puppet 44 | service: name=puppet state=started 45 | 46 | - name: Update the Server 47 | command: /usr/bin/yum -y update 48 | 49 | handlers: 50 | - name: Enable Katello Agent 51 | service: name=goferd enabled=yes 52 | 53 | - name: Start Katello Agent 54 | service: name=goferd state=started 55 | 56 | - name: Enable Puppet 57 | service: name=puppet enabled=yes 58 | -------------------------------------------------------------------------------- /chapter16/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot1.png -------------------------------------------------------------------------------- /chapter16/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot10.png -------------------------------------------------------------------------------- /chapter16/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot11.png -------------------------------------------------------------------------------- /chapter16/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot12.png -------------------------------------------------------------------------------- /chapter16/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot2.png -------------------------------------------------------------------------------- /chapter16/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot3.png -------------------------------------------------------------------------------- /chapter16/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot4.png -------------------------------------------------------------------------------- /chapter16/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot5.png -------------------------------------------------------------------------------- /chapter16/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot6.png -------------------------------------------------------------------------------- /chapter16/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot7.png -------------------------------------------------------------------------------- /chapter16/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot8.png -------------------------------------------------------------------------------- /chapter16/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter16/images/screenshot9.png -------------------------------------------------------------------------------- /chapter16/scripts/register_satellite.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Description: Create configured host record in Satellite 6 3 | # 4 | 5 | @method = 'register_satellite' 6 | 7 | require 'rest-client' 8 | require 'json' 9 | require 'openssl' 10 | require 'base64' 11 | 12 | 13 | begin 14 | # ---------------------------------------------------- 15 | 16 | def query_id (uri, field, content) 17 | 18 | url = URI.escape("#{@uri_base}/#{uri}?search=#{field}=\"#{content}\"") 19 | request = RestClient::Request.new( 20 | method: :get, 21 | url: url, 22 | headers: @headers, 23 | verify_ssl: OpenSSL::SSL::VERIFY_NONE 24 | ) 25 | 26 | id = nil 27 | rest_result = request.execute 28 | json_parse = JSON.parse(rest_result) 29 | 30 | subtotal = json_parse['subtotal'].to_i 31 | if subtotal == 1 32 | id = json_parse['results'][0]['id'].to_s 33 | elsif subtotal.zero? 34 | $evm.log(:error, "Query to #{url} failed, no result") 35 | id = -1 36 | elsif subtotal > 1 37 | $evm.log(:error, "Query to #{url} returned multiple results") 38 | id = -1 39 | else 40 | $evm.log(:error, "Query to #{url} failed, unknown condition") 41 | id = -1 42 | end 43 | id 44 | end 45 | 46 | # ---------------------------------------------------- 47 | 48 | servername = $evm.object['servername'] 49 | username = $evm.object['username'] 50 | password = $evm.object.decrypt('password') 51 | organization = $evm.object['organization'] 52 | location = $evm.object['location'] 53 | 54 | prov = $evm.root['miq_provision'] 55 | template = prov.source 56 | vm = prov.destination 57 | # 58 | # Only register if the provisioning template is linux 59 | # 60 | if template.platform == "linux" 61 | # 62 | # Pick a host-group based on the operating system being provisioned 63 | # 64 | if vm.operating_system.product_name == 'Red Hat Enterprise Linux 6 (64-bit)' 65 | hostgroup = 'Generic_RHEL6_Servers' 66 | elsif vm.operating_system.product_name == 'Red Hat Enterprise Linux 7 (64-bit)' 67 | hostgroup = 'Generic_RHEL7_Servers' 68 | else 69 | raise "Unrecognised Operating System Name" 70 | end 71 | 72 | @uri_base = "https://#{servername}/api/v2" 73 | @headers = { 74 | :content_type => 'application/json', 75 | :accept => 'application/json;version=2', 76 | :authorization => "Basic #{Base64.strict_encode64("#{username}:#{password}")}" 77 | } 78 | # 79 | # Get the host-group id 80 | # 81 | $evm.log(:info, "Getting hostgroup id for '#{hostgroup}' from Satellite") 82 | hostgroup_id = query_id("hostgroups", "name", hostgroup) 83 | raise "Cannot determine hostgroup id for '#{hostgroup}'" if hostgroup_id == -1 84 | $evm.log(:info, "hostgroup_id: #{hostgroup_id}") 85 | 86 | # 87 | # Get the location id 88 | # 89 | $evm.log(:info, "Getting location id for '#{location}' from Satellite") 90 | location_id = query_id("locations", "name", location) 91 | raise "Cannot determine location id for '#{location}'" if location_id == -1 92 | $evm.log(:info, "location_id: #{location_id}") 93 | 94 | # 95 | # Get the organization id 96 | # 97 | $evm.log(:info, "Getting organization id for '#{organization}' from Satellite") 98 | organization_id = query_id("organizations", "name", organization) 99 | raise "Cannot determine organization id for '#{organization}'" if organization_id == -1 100 | $evm.log(:info, "organization_id: #{organization_id}") 101 | # 102 | # Create the host record 103 | # 104 | hostinfo = { 105 | :name => vm.name, 106 | :mac => vm.mac_addresses[0], 107 | :hostgroup_id => hostgroup_id, 108 | :location_id => location_id, 109 | :organization_id => organization_id, 110 | :build => 'false' 111 | } 112 | $evm.log(:info, "Creating host record in Satellite with the following details: #{hostinfo}") 113 | 114 | uri = "#{@uri_base}/hosts" 115 | request = RestClient::Request.new( 116 | method: :post, 117 | url: uri, 118 | headers: @headers, 119 | verify_ssl: OpenSSL::SSL::VERIFY_NONE, 120 | payload: { host: hostinfo }.to_json 121 | ) 122 | rest_result = request.execute 123 | $evm.log(:info, "return code => <#{rest_result.code}>") 124 | end 125 | vm.start 126 | $evm.root['ae_result'] = 'ok' 127 | exit MIQ_OK 128 | rescue RestClient::Exception => err 129 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 130 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 131 | $evm.root['ae_result'] = 'error' 132 | $evm.root['ae_reason'] = "The REST request failed with code: #{err.response.code}" unless err.response.nil? 133 | exit MIQ_STOP 134 | rescue => err 135 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 136 | $evm.root['ae_result'] = 'error' 137 | $evm.root['ae_reason'] = "Error registering with Satellite: #{err.message}" 138 | exit MIQ_ERROR 139 | end 140 | -------------------------------------------------------------------------------- /chapter17/approval_and_quota.md: -------------------------------------------------------------------------------- 1 | ## Approval and Quota 2 | 3 | ### Approval 4 | 5 | The approval process for a Service Provision Request is entered as a result of the `/ManageIQ/System/Policy/ServiceTemplateProvisionRequest_created` policy being run from a _request\_created_ event. This results in a Service Provisioning Profile lookup to read the value of the **auto\_approval\_state\_machine** attribute, which by default is `ServiceProvisionRequestApproval` for a Service Provision Request. The second relationship from the event runs the `Default` Instance of this State Machine. 6 |

7 | 8 | ![screenshot](images/screenshot11.png) 9 | 10 |
11 | The Schema for the `ServiceProvisionRequestApproval` State Machine is: 12 |
13 | 14 | ![screenshot](images/screenshot12.png?) 15 | 16 |
17 | The `Default` Instance has the following Field values: 18 |
19 | 20 | ![screenshot](images/screenshot13.png) 21 |
22 | 23 | This Instance will auto-approve all Service Provisioning Requests. 24 | 25 | ### Quota 26 | 27 | Quota checking for a Service Provision Request in ManageIQ _Capablanca_ (CloudForms Management Engine 5.5) uses the same consolidated quota mechanism as described in [Quota Management](../chapter15/quota_management.md). 28 | 29 | The quota checking process is entered as a result of the `/System/Policy/ServiceTemplateProvisionRequest_starting` policy being run from a _request\_starting_ event. The policy Instance jumps straight to the `quota` State Machine from its **rel2** Relationship: 30 |

31 | 32 | ![screenshot](images/screenshot90.png) 33 | 34 | Currently the code does nothing if a quota is exceeded for a Service provision, but we can add 35 | a policy `/System/Policy/ServiceTemplateProvisionRequest_denied` to our own domain, that contains a Relationship to the `/Service/Provisioning/Email/ServiceTemplateProvisionRequest_Denied` email Instance in the `RedHat` Domain. Of course we should ideally copy this Email Instance into our own Domain as well so that we can customise the **from\_email\_addres**, **to\_email\_addres**, and **signature** attributes. 36 | -------------------------------------------------------------------------------- /chapter17/catalogiteminitialization.md: -------------------------------------------------------------------------------- 1 | ## Catalog{Item,Bundle}Initialization 2 | 3 | Two of the out-of-the-box Service Provisioning State Machines are `CatalogItemInitialization` and `CatalogBundleInitialization`. They have been created to simplify the process of creating full-featured Service Catalog Items and Bundles, fronted by rich service dialogs, without the need for any Ruby scripting. 4 | 5 | These two State Machine Instances, and their supporting Methods, have been completely re-written for ManageIQ _Botvinnik_ (CloudForms Management Engine 5.4). We will discuss the use of the `CatalogItemInitialization` Instance in detail in this section. 6 | 7 | ### CatalogItemInitialization 8 | 9 | The `CatalogItemInitialization` State Machine can be specified when we create a Service Catalog _Item_, i.e. an orderable entry in the Service Catalog that provisions a single type of VM (although it may result in multiple VMs of the same type being provisioned). 10 | 11 | The State Machine greatly simplifies the process of customising the provisioned VMs, using values read from the service dialog. It does this by setting options\_hash values or tags in the child and grandchild _miq\_request\_task_ objects, from specially constructed service dialog element names. It also allows us to specify a new unique name and description for the resulting Service. 12 | 13 | The schema for the `CatalogItemInitialization` Instance is as follows: 14 | 15 | 16 | ![screenshot](images/screenshot7.png?) 17 | 18 |
19 | We can see that the schema uses the **pre1** and **pre2** states to add a dialog parser (common between the `CatalogItemInitialization` and `CatalogBundleInitialization` State Machines), and the `CatalogItemInitialization` Method to set the child object options\_hash values and/or tags accordingly. 20 | 21 | The `CatalogItemInitialization` Method itself relies on the dialog inputs having been parsed correctly by `dialog_parser`, and this requires us to use a 'standard' naming convention for our service dialog elements. 22 | 23 | #### Service Dialog Element Naming Convention 24 | 25 | To perform the service dialog -> options_hash or tag substitution correctly, we must name our service dialog elements in a particular way, depending on whether the element is requesting input for a single option or tag, or a comma-separated list (array) of options or tags. 26 | 27 | ##### Single Option 28 | 29 | The simplest service dialog element to process is one that prompts for the value of a single options\_hash key. We name the service dialog element as: 30 | 31 | **option\_0\__key\_name_** (for backwards compatibility with CloudForms Management Engine 5.3) 32 | 33 | or just 34 | 35 | **_key\_name_** (valid for CloudForms Management Engine 5.4 and later) 36 | 37 | For example if we create a service dialog element as follows: 38 | 39 | ![screenshot](images/screenshot9.png) 40 | 41 | The resulting value input at run-time will be will be propagated to the child task's options hash as: 42 | 43 | ``` 44 | miq_request_task.options[:vm_memory] 45 | ``` 46 | 47 | 48 | The '0' in the dialog name refers to the item sequence number when provisioning. For a service dialog fronting a single catalog _Item_ this will always be zero. For a service dialog fronting a catalog _Bundle_ comprising several Items, the sequence number indicates which of the component Items the dialog option should be passed to (an element with a name sequence of '0' will be propagated to all Items). 49 | 50 | Several **key\_name** values are recognised and special-cased by the `CatalogItemInitialization` Method. We can name a text box element as either **vm\_name** or **vm\_target\_name**, and the resulting text string input value will be propagated to all of: 51 | 52 | ``` 53 | miq_request_task.options[:vm_target_name] 54 | miq_request_task.options[:vm_target_hostname] 55 | miq_request_task.options[:vm_name] 56 | miq_request_task.options[:linux_host_name] 57 | ``` 58 | 59 | If we name a text box element as **service\_name**, then the resulting service will be named from the text value of this element. 60 | 61 | If we name a text box element as **service\_description**, then the resulting service description will be updated from the text value of this element. 62 | 63 | ##### Single Tag 64 | 65 | We can also create a text box service dialog element to apply a single tag. We name the service dialog element as: 66 | 67 | **tag\_0\__tag\_category_** 68 | 69 | For example: 70 | 71 | ![screenshot](images/screenshot10.png) 72 | 73 | The value input into the service dialog element at run-time should be a tag within this tag category. When an element of this type is processed by the `CatalogItemInitialization` Method, if either the category or tag don't currently exist, they will be created. 74 | 75 | ### CatalogBundleInitialization 76 | 77 | The `CatalogBundleInitialization` State Machine should be specified when we create a Service Catalog _Bundle_. 78 | 79 | The schema for the `CatalogBundleInitialization` Instance is the same as for `CatalogItemInitialization`, except that the **pre2** stage calls the `CatalogBundleInitialization` Method. 80 | 81 | The `CatalogBundleInitialization` Method passes the service dialog element values on to each catalog item's `CatalogItemInitialization` Method, which is still required in order to set the miq\_request\_task's options hash keys for the provision of that catalog item. 82 | -------------------------------------------------------------------------------- /chapter17/custom_state_machines.md: -------------------------------------------------------------------------------- 1 | ## Custom State Machines 2 | 3 | There are times when an out-of-the-box Service Provision State Machine does not provide the flexibility that we need to create the service that we require. An example of this would be if we wish to offer our users the choice of how many VMs to provision when ordering from a Service Catalog, or to dynamically select the template to provision from. 4 | 5 | When we create a new Service Catalog Item, we are presented with a drop-down list of Catalog Item Types to choose from: 6 | 7 | ![screenshot](images/screenshot26.png) 8 | 9 | If we pick any of the Cloud or Infrastructure Provider types, we are prompted to select a template or image to provision from, and we complete the dialog options to specify how we would like that VM provisioned. Once we save the new Catalog Item, these selections are "hard-coded" in an _MiqProvisionRequestTemplate_ object that contains the "request" options hash (containing `:number_of_vms` for example), and a `.source` association to the template to be provisioned from. We have no way of changing these when we order the service. 10 | 11 | Fortunately there's a **Generic** Catalog Item Type that we can use to create a custom State Machine, and we can use this to assemble arguments for, and call, `$evm.execute('create_provision_request')` (see [Creating Provisoning Requests Programmatically](../chapter15/create_provision_request.md)). 12 | 13 | By hand-rolling the arguments to create\_provision\_request, we have complete control over the VM Provision Request. We can specify the template name or `:number_of_vms` dynamically at run-time if required. 14 | 15 | ### Importing an Example 16 | 17 | Kevin Morey has created an example of such a State Machine in his excellent [CloudFormsPOC](https://github.com/ramrexx/CloudFormsPOC/wiki) code collection. We'll import this into a new `SampleCode` domain, and create our own Service Catalog Item from it. 18 | 19 | #### Importing the Code 20 | 21 | First we must create the new domain into which we can import the code. We won't enable this new domain as we'll copy any code that we want into our own domain: 22 | 23 | ![screenshot](images/screenshot54.png) 24 | 25 | After following Kevin's instructions on downloading and zipping the Automation code, we can import it from the **Automate -> Import / Export** menu: 26 | 27 | ![screenshot](images/screenshot55.png) 28 | 29 | Kevin has made quite a few enhancements, so we need to import some Methods and Instances from four different namespaces in the imported domain into our own domain. We'll be using a VMware provider for this example, so we'll copy the VMware-specific customisations. 30 | 31 | From the `Infrastructure` namespace: 32 | 33 | ![screenshot](images/screenshot56.png) 34 | 35 |
36 | From the `Integration` namespace: 37 | 38 | ![screenshot](images/screenshot57.png) 39 | 40 |
41 | From the `Service` namespace: 42 | 43 | ![screenshot](images/screenshot58.png) 44 | 45 |
46 | From the `System` namespace: 47 | 48 | ![screenshot](images/screenshot59.png) 49 | 50 | #### Importing the Service Dialog 51 | 52 | Now we'll import the **VMware - Generic - Build\_VMProvisionRequest** Service Dialog from Kevin's code collection. Go to **Automate -> Customization**, and to the **Import/Export** section in the accordion. Using **Choose File**, select `ServiceDialogs/VMware - Generic - Build_VMProvisionRequest.yml` from the expanded `CloudFormsPOC-master.zip`. Click **Upload**. 53 | 54 | We're going to edit the Service Dialog to simplify it for our needs. Edit the dialog, and remove all but the following elements: 55 |

56 | 57 | ![screenshot](images/screenshot60.png) 58 | 59 |
60 | The **Template** element is dynamic. Ensure that its Entry Point is our newly imported `/Integration/VMware/DynamicDropDowns/List_VMware_Templates` Instance, and ensure that **Include Domain prefix in the path** is checked. 61 | 62 | ### Creating the Service Catalog Item 63 | 64 | We'll create the new Service Catalog Item as we did before, but we'll pick **Generic** as the Catalog Item Type. 65 | 66 | We enter a Name and Description, and check **Display in Catalog** to reveal the other options. We select a suitable Catalog, and the new **VMware - Generic - Build\_VMProvisionRequest** Dialog from the drop-downs. 67 | 68 | Finally we select `/Service/Provisioning/StateMachines/ServiceProvision_Template/VMware_Build_VMProvisionRequest` from our Domain as the Provisioning Entry Point, and ensure that **Include Domain prefix in the path** is checked. Save the new Catalog Item. 69 | 70 | ### Ordering the Service 71 | 72 | When we order the Service Catalog Item, we see that the Dialog has enumerated all templates on our Provider, and presents them to us as a drop-down list. We also get the option to select the number of VMs to provision: 73 |

74 | 75 | ![screenshot](images/screenshot61.png) 76 | 77 | We complete the dialog and click **Submit**, and after a while our new Service is ready: 78 |

79 | 80 | ![screenshot](images/screenshot62.png) 81 | 82 | Success! 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /chapter17/images/Service Objects 2.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/Service Objects 2.graffle -------------------------------------------------------------------------------- /chapter17/images/Service Objects.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/Service Objects.graffle -------------------------------------------------------------------------------- /chapter17/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot1.png -------------------------------------------------------------------------------- /chapter17/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot10.png -------------------------------------------------------------------------------- /chapter17/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot11.png -------------------------------------------------------------------------------- /chapter17/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot12.png -------------------------------------------------------------------------------- /chapter17/images/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot13.png -------------------------------------------------------------------------------- /chapter17/images/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot14.png -------------------------------------------------------------------------------- /chapter17/images/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot15.png -------------------------------------------------------------------------------- /chapter17/images/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot16.png -------------------------------------------------------------------------------- /chapter17/images/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot17.png -------------------------------------------------------------------------------- /chapter17/images/screenshot18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot18.png -------------------------------------------------------------------------------- /chapter17/images/screenshot19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot19.png -------------------------------------------------------------------------------- /chapter17/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot2.png -------------------------------------------------------------------------------- /chapter17/images/screenshot20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot20.png -------------------------------------------------------------------------------- /chapter17/images/screenshot21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot21.png -------------------------------------------------------------------------------- /chapter17/images/screenshot22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot22.png -------------------------------------------------------------------------------- /chapter17/images/screenshot23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot23.png -------------------------------------------------------------------------------- /chapter17/images/screenshot24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot24.png -------------------------------------------------------------------------------- /chapter17/images/screenshot25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot25.png -------------------------------------------------------------------------------- /chapter17/images/screenshot26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot26.png -------------------------------------------------------------------------------- /chapter17/images/screenshot27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot27.png -------------------------------------------------------------------------------- /chapter17/images/screenshot28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot28.png -------------------------------------------------------------------------------- /chapter17/images/screenshot29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot29.png -------------------------------------------------------------------------------- /chapter17/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot3.png -------------------------------------------------------------------------------- /chapter17/images/screenshot30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot30.png -------------------------------------------------------------------------------- /chapter17/images/screenshot31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot31.png -------------------------------------------------------------------------------- /chapter17/images/screenshot32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot32.png -------------------------------------------------------------------------------- /chapter17/images/screenshot33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot33.png -------------------------------------------------------------------------------- /chapter17/images/screenshot34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot34.png -------------------------------------------------------------------------------- /chapter17/images/screenshot35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot35.png -------------------------------------------------------------------------------- /chapter17/images/screenshot36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot36.png -------------------------------------------------------------------------------- /chapter17/images/screenshot37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot37.png -------------------------------------------------------------------------------- /chapter17/images/screenshot38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot38.png -------------------------------------------------------------------------------- /chapter17/images/screenshot39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot39.png -------------------------------------------------------------------------------- /chapter17/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot4.png -------------------------------------------------------------------------------- /chapter17/images/screenshot40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot40.png -------------------------------------------------------------------------------- /chapter17/images/screenshot41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot41.png -------------------------------------------------------------------------------- /chapter17/images/screenshot42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot42.png -------------------------------------------------------------------------------- /chapter17/images/screenshot43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot43.png -------------------------------------------------------------------------------- /chapter17/images/screenshot44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot44.png -------------------------------------------------------------------------------- /chapter17/images/screenshot45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot45.png -------------------------------------------------------------------------------- /chapter17/images/screenshot46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot46.png -------------------------------------------------------------------------------- /chapter17/images/screenshot47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot47.png -------------------------------------------------------------------------------- /chapter17/images/screenshot48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot48.png -------------------------------------------------------------------------------- /chapter17/images/screenshot49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot49.png -------------------------------------------------------------------------------- /chapter17/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot5.png -------------------------------------------------------------------------------- /chapter17/images/screenshot50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot50.png -------------------------------------------------------------------------------- /chapter17/images/screenshot51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot51.png -------------------------------------------------------------------------------- /chapter17/images/screenshot52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot52.png -------------------------------------------------------------------------------- /chapter17/images/screenshot53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot53.png -------------------------------------------------------------------------------- /chapter17/images/screenshot54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot54.png -------------------------------------------------------------------------------- /chapter17/images/screenshot55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot55.png -------------------------------------------------------------------------------- /chapter17/images/screenshot56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot56.png -------------------------------------------------------------------------------- /chapter17/images/screenshot57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot57.png -------------------------------------------------------------------------------- /chapter17/images/screenshot58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot58.png -------------------------------------------------------------------------------- /chapter17/images/screenshot59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot59.png -------------------------------------------------------------------------------- /chapter17/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot6.png -------------------------------------------------------------------------------- /chapter17/images/screenshot60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot60.png -------------------------------------------------------------------------------- /chapter17/images/screenshot61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot61.png -------------------------------------------------------------------------------- /chapter17/images/screenshot62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot62.png -------------------------------------------------------------------------------- /chapter17/images/screenshot63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot63.png -------------------------------------------------------------------------------- /chapter17/images/screenshot64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot64.png -------------------------------------------------------------------------------- /chapter17/images/screenshot65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot65.png -------------------------------------------------------------------------------- /chapter17/images/screenshot66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot66.png -------------------------------------------------------------------------------- /chapter17/images/screenshot67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot67.png -------------------------------------------------------------------------------- /chapter17/images/screenshot68.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot68.png -------------------------------------------------------------------------------- /chapter17/images/screenshot69.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot69.png -------------------------------------------------------------------------------- /chapter17/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot7.png -------------------------------------------------------------------------------- /chapter17/images/screenshot70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot70.png -------------------------------------------------------------------------------- /chapter17/images/screenshot71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot71.png -------------------------------------------------------------------------------- /chapter17/images/screenshot72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot72.png -------------------------------------------------------------------------------- /chapter17/images/screenshot73.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot73.png -------------------------------------------------------------------------------- /chapter17/images/screenshot74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot74.png -------------------------------------------------------------------------------- /chapter17/images/screenshot75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot75.png -------------------------------------------------------------------------------- /chapter17/images/screenshot76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot76.png -------------------------------------------------------------------------------- /chapter17/images/screenshot77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot77.png -------------------------------------------------------------------------------- /chapter17/images/screenshot78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot78.png -------------------------------------------------------------------------------- /chapter17/images/screenshot79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot79.png -------------------------------------------------------------------------------- /chapter17/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot8.png -------------------------------------------------------------------------------- /chapter17/images/screenshot80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot80.png -------------------------------------------------------------------------------- /chapter17/images/screenshot81.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot81.png -------------------------------------------------------------------------------- /chapter17/images/screenshot82.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot82.png -------------------------------------------------------------------------------- /chapter17/images/screenshot83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot83.png -------------------------------------------------------------------------------- /chapter17/images/screenshot84.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot84.png -------------------------------------------------------------------------------- /chapter17/images/screenshot85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot85.png -------------------------------------------------------------------------------- /chapter17/images/screenshot86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot86.png -------------------------------------------------------------------------------- /chapter17/images/screenshot87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot87.png -------------------------------------------------------------------------------- /chapter17/images/screenshot88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot88.png -------------------------------------------------------------------------------- /chapter17/images/screenshot89.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot89.png -------------------------------------------------------------------------------- /chapter17/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot9.png -------------------------------------------------------------------------------- /chapter17/images/screenshot90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/screenshot90.png -------------------------------------------------------------------------------- /chapter17/images/service_objects_detailed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/service_objects_detailed.png -------------------------------------------------------------------------------- /chapter17/images/task_hierarchy.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/task_hierarchy.graffle -------------------------------------------------------------------------------- /chapter17/images/task_hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter17/images/task_hierarchy.png -------------------------------------------------------------------------------- /chapter17/scripts/add_to_service.rb: -------------------------------------------------------------------------------- 1 | begin 2 | parent_service_id = $evm.root['dialog_service'] 3 | parent_service = $evm.vmdb('service').find_by_id(parent_service_id) 4 | if parent_service.nil? 5 | $evm.log(:error, "Can't find service with ID: #{parent_service_id}") 6 | exit MIQ_ERROR 7 | else 8 | case $evm.root['vmdb_object_type'] 9 | when 'service' 10 | $evm.log(:info, "Adding Service #{$evm.root['service'].name} to #{parent_service.name}") 11 | $evm.root['service'].parent_service = parent_service 12 | when 'vm' 13 | vm = $evm.root['vm'] 14 | # 15 | # See if the VM is already part of a service 16 | # 17 | unless vm.service.nil? 18 | old_service = vm.service 19 | vm.remove_from_service 20 | if old_service.v_total_vms.zero? 21 | $evm.log(:info, "Old service #{old_service.name} is now empty, removing it from VMDB") 22 | old_service.remove_from_vmdb 23 | end 24 | end 25 | $evm.log(:info, "Adding VM #{vm.name} to #{parent_service.name}") 26 | vm.add_to_service(parent_service) 27 | end 28 | end 29 | exit MIQ_OK 30 | rescue => err 31 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 32 | exit MIQ_ERROR 33 | end 34 | 35 | -------------------------------------------------------------------------------- /chapter17/scripts/get_configuration.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | 28 | parameter_id = $evm.object['dialog_parameter_id'] 29 | hostgroup_id = $evm.object['dialog_hostgroup_id'] 30 | hostname = $evm.object['dialog_vm_name'] 31 | 32 | uri_base = "https://#{servername}/api/v2" 33 | # 34 | # Get the domain name for the hostgroup 35 | # 36 | rest_return = rest_action("#{uri_base}/hostgroups/#{hostgroup_id}", :get) 37 | domain_name = rest_return['domain_name'] 38 | match = "fqdn=#{hostname}.#{domain_name}" 39 | # 40 | # See if the override match already exists 41 | # 42 | rest_return = rest_action("#{uri_base}/smart_class_parameters/#{parameter_id}/override_values", :get) 43 | value_string = "" 44 | if rest_return['total'] > 0 45 | rest_return['results'].each do |override_value| 46 | if override_value['match'] == match 47 | value_string = override_value['value'] 48 | break 49 | end 50 | end 51 | end 52 | 53 | list_values = { 54 | 'required' => false, 55 | 'protected' => false, 56 | 'read_only' => false, 57 | 'value' => value_string, 58 | } 59 | list_values.each { |key, value| $evm.object[key] = value } 60 | 61 | exit MIQ_OK 62 | rescue RestClient::Exception => err 63 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 64 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 65 | $evm.root['ae_reason'] = "The REST request failed with code: #{err.response.code}" unless err.response.nil? 66 | $evm.root['ae_result'] = 'error' 67 | exit MIQ_STOP 68 | rescue => err 69 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 70 | $evm.root['ae_reason'] = "Unspecified error, see automation.log for backtrace" 71 | $evm.root['ae_result'] = 'error' 72 | exit MIQ_STOP 73 | end -------------------------------------------------------------------------------- /chapter17/scripts/list_activationkeys.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | hostgroup_id = $evm.object['dialog_hostgroup_id'] 28 | 29 | uri_base = "https://#{servername}/api/v2" 30 | 31 | values_hash = {} 32 | if hostgroup_id.nil? 33 | values_hash['!'] = "Select a Host Group and click 'Refresh'" 34 | else 35 | rest_return = rest_action("#{uri_base}/hostgroups/#{hostgroup_id}/parameters", :get) 36 | rest_return['results'].each do |hostgroup_parameter| 37 | if hostgroup_parameter['name'].to_s == "kt_activation_keys" 38 | hostgroup_parameter['value'].split(',').each do |activationkey| 39 | $evm.log(:info, "Found Activation Key: '#{activationkey}'") 40 | values_hash[activationkey] = activationkey 41 | end 42 | end 43 | end 44 | if values_hash.length > 0 45 | if values_hash.length > 1 46 | values_hash['!'] = '-- select from list --' 47 | end 48 | else 49 | values_hash['!'] = 'This Host Group has no Activation Keys' 50 | end 51 | end 52 | 53 | list_values = { 54 | 'sort_by' => :description, 55 | 'data_type' => :string, 56 | 'required' => true, 57 | 'values' => values_hash 58 | } 59 | list_values.each { |key, value| $evm.object[key] = value } 60 | exit MIQ_OK 61 | rescue RestClient::Exception => err 62 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 63 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 64 | exit MIQ_STOP 65 | rescue => err 66 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 67 | exit MIQ_STOP 68 | end -------------------------------------------------------------------------------- /chapter17/scripts/list_hostgroups.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | 28 | uri_base = "https://#{servername}/api/v2" 29 | 30 | rest_return = rest_action("#{uri_base}/hostgroups", :get) 31 | 32 | values_hash = {} 33 | 34 | if rest_return['total'] > 0 35 | if rest_return['total'] > 1 36 | values_hash['!'] = '-- select from list --' 37 | end 38 | rest_return['results'].each do |hostgroup| 39 | $evm.log(:info, "Found Host Group '#{hostgroup['name']}' with ID: #{hostgroup['id'].to_s}") 40 | values_hash[hostgroup['id'].to_s] = hostgroup['name'] 41 | end 42 | else 43 | values_hash['!'] = 'No hostgroups are available' 44 | end 45 | 46 | list_values = { 47 | 'sort_by' => :description, 48 | 'data_type' => :string, 49 | 'required' => true, 50 | 'values' => values_hash 51 | } 52 | list_values.each { |key, value| $evm.object[key] = value } 53 | exit MIQ_OK 54 | rescue RestClient::Exception => err 55 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 56 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 57 | exit MIQ_STOP 58 | rescue => err 59 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 60 | exit MIQ_STOP 61 | end -------------------------------------------------------------------------------- /chapter17/scripts/list_hostgroups_from_ems.rb: -------------------------------------------------------------------------------- 1 | begin 2 | 3 | values_hash = {} 4 | hostgroups = $evm.vmdb(:configuration_profile).all 5 | 6 | if hostgroups.length > 0 7 | if hostgroups.length > 1 8 | values_hash['!'] = '-- select from list --' 9 | end 10 | hostgroups.each do |hostgroup| 11 | $evm.log(:info, "Found Host Group '#{hostgroup.name}' with ID: #{hostgroup.manager_ref}") 12 | values_hash[hostgroup.manager_ref] = hostgroup.name 13 | end 14 | else 15 | values_hash['!'] = 'No hostgroups are available' 16 | end 17 | 18 | list_values = { 19 | 'sort_by' => :description, 20 | 'data_type' => :string, 21 | 'required' => true, 22 | 'values' => values_hash 23 | } 24 | list_values.each { |key, value| $evm.object[key] = value } 25 | exit MIQ_OK 26 | rescue => err 27 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 28 | exit MIQ_STOP 29 | end 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /chapter17/scripts/list_puppetclasses.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | hostgroup_id = $evm.object['dialog_hostgroup_id'] 28 | 29 | uri_base = "https://#{servername}/api/v2" 30 | 31 | values_hash = {} 32 | if hostgroup_id.nil? 33 | values_hash['!'] = "Select a Host Group and click 'Refresh'" 34 | else 35 | rest_return = rest_action("#{uri_base}/hostgroups/#{hostgroup_id}/puppetclasses", :get) 36 | if rest_return['total'] > 0 37 | if rest_return['total'] > 1 38 | values_hash['!'] = '-- select from list --' 39 | end 40 | rest_return['results'].each do |classname, classinfo| 41 | $evm.log(:info, "Found Puppet Class '#{classname}' with ID: #{classinfo[0]['id'].to_s}") 42 | $evm.log(:info, "Classname: #{classname}") 43 | values_hash[classinfo[0]['id'].to_s] = classname 44 | end 45 | else 46 | values_hash['!'] = 'No Puppet Classes are defined for this Hostgroup' 47 | end 48 | end 49 | 50 | list_values = { 51 | 'sort_by' => :description, 52 | 'data_type' => :string, 53 | 'required' => true, 54 | 'values' => values_hash 55 | } 56 | list_values.each { |key, value| $evm.object[key] = value } 57 | exit MIQ_OK 58 | rescue RestClient::Exception => err 59 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 60 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 61 | exit MIQ_STOP 62 | rescue => err 63 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 64 | exit MIQ_STOP 65 | end -------------------------------------------------------------------------------- /chapter17/scripts/list_services.rb: -------------------------------------------------------------------------------- 1 | begin 2 | 3 | def get_current_group_rbac_array(user, rbac_array=[]) 4 | unless user.current_group.filters.blank? 5 | user.current_group.filters['managed'].flatten.each do |filter| 6 | next unless /(?\w*)\/(?\w*)$/i =~ filter 7 | rbac_array << {category=>tag} 8 | end 9 | end 10 | $evm.log(:info, "rbac filters: #{rbac_array}") 11 | rbac_array 12 | end 13 | 14 | def service_visible?(rbac_array, service) 15 | rbac_array.each do |rbac_hash| 16 | rbac_hash.each {|category, tag| return false unless service.tagged_with?(category, tag)} 17 | end 18 | $evm.log(:info, "Service: #{service.name} is visible to this user") 19 | true 20 | end 21 | 22 | user = $evm.root['user'] 23 | rbac_array = get_current_group_rbac_array(user) 24 | values_hash = {} 25 | visible_services = [] 26 | 27 | $evm.vmdb(:service).find(:all).each do |service| 28 | $evm.log(:info, "Found service: #{service.name}") 29 | if service_visible?(rbac_array, service) 30 | visible_services << service 31 | end 32 | end 33 | if visible_services.length > 0 34 | if visible_services.length > 1 35 | values_hash['!'] = '-- select from list --' 36 | end 37 | visible_services.each do |service| 38 | values_hash[service.id] = service.name 39 | end 40 | else 41 | values_hash['!'] = 'No services are available' 42 | end 43 | 44 | list_values = { 45 | 'sort_by' => :description, 46 | 'data_type' => :string, 47 | 'required' => true, 48 | 'values' => values_hash 49 | } 50 | list_values.each { |key, value| $evm.object[key] = value } 51 | exit MIQ_OK 52 | rescue => err 53 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 54 | exit MIQ_STOP 55 | end 56 | 57 | -------------------------------------------------------------------------------- /chapter17/scripts/list_smart_class_parameters.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | hostgroup_id = $evm.object['dialog_hostgroup_id'] 28 | puppet_class_id = $evm.object['dialog_puppet_class_id'] 29 | 30 | uri_base = "https://#{servername}/api/v2" 31 | 32 | values_hash = {} 33 | if puppet_class_id.nil? 34 | values_hash['!'] = "Select a Puppet Class and click 'Refresh'" 35 | else 36 | call_string = "#{uri_base}/hostgroups/#{hostgroup_id}/smart_class_parameters" 37 | rest_return = rest_action(call_string, :get) 38 | rest_return['results'].each do |parameter| 39 | $evm.log(:info, "Found smart class parameter '#{parameter['parameter']}' with ID: #{parameter['id'].to_s}") 40 | # 41 | # Retrieve the details of this smart class parameter 42 | # to find out which puppet class it's associated with 43 | # 44 | call_string = "#{uri_base}/hostgroups/#{hostgroup_id}/smart_class_parameters/#{parameter['id']}" 45 | parameter_details = rest_action(call_string, :get) 46 | if parameter_details['puppetclass']['id'].to_s == puppet_class_id 47 | $evm.log(:info, "Parameter #{parameter['id'].to_s} matches the requested Puppet Class") 48 | values_hash[parameter['id'].to_s] = parameter_details['parameter'] 49 | end 50 | end 51 | if values_hash.length > 0 52 | if values_hash.length > 1 53 | values_hash['!'] = '-- select from list --' 54 | end 55 | else 56 | values_hash['!'] = 'This Puppet class has no Smart Class Parameters' 57 | end 58 | end 59 | 60 | list_values = { 61 | 'sort_by' => :description, 62 | 'data_type' => :string, 63 | 'required' => true, 64 | 'values' => values_hash 65 | } 66 | list_values.each { |key, value| $evm.object[key] = value } 67 | exit MIQ_OK 68 | rescue RestClient::Exception => err 69 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 70 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 71 | exit MIQ_STOP 72 | rescue => err 73 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 74 | exit MIQ_STOP 75 | end -------------------------------------------------------------------------------- /chapter17/scripts/register_satellite.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def query_id (uri, field, content) 9 | url = URI.escape("#{@uri_base}/#{uri}?search=#{field}=\"#{content}\"") 10 | request = RestClient::Request.new( 11 | method: :get, 12 | url: url, 13 | headers: @headers, 14 | verify_ssl: OpenSSL::SSL::VERIFY_NONE 15 | ) 16 | 17 | id = nil 18 | rest_result = request.execute 19 | json_parse = JSON.parse(rest_result) 20 | 21 | subtotal = json_parse['subtotal'].to_i 22 | if subtotal == 1 23 | id = json_parse['results'][0]['id'].to_s 24 | elsif subtotal.zero? 25 | $evm.log(:error, "Query to #{url} failed, no result") 26 | id = -1 27 | elsif subtotal > 1 28 | $evm.log(:error, "Query to #{url} returned multiple results") 29 | id = -1 30 | else 31 | $evm.log(:error, "Query to #{url} failed, unknown condition") 32 | id = -1 33 | end 34 | id 35 | end 36 | 37 | servername = $evm.object['servername'] 38 | username = $evm.object['username'] 39 | password = $evm.object.decrypt('password') 40 | organization = $evm.object['organization'] 41 | location = $evm.object['location'] 42 | 43 | prov = $evm.root['miq_provision'] 44 | template = prov.source 45 | vm = prov.destination 46 | # 47 | # Only register if the provisioning template is linux 48 | # 49 | if template.platform == "linux" 50 | # 51 | # Only register with Satellite if we've been passed a hostgroup ID from a service dialog 52 | # 53 | hostgroup_id = $evm.root['miq_provision'].get_option(:dialog_hostgroup_id) 54 | unless hostgroup_id.nil? 55 | 56 | @uri_base = "https://#{servername}/api/v2" 57 | @headers = { 58 | :content_type => 'application/json', 59 | :accept => 'application/json;version=2', 60 | :authorization => "Basic #{Base64.strict_encode64("#{username}:#{password}")}" 61 | } 62 | # 63 | # Get the location id 64 | # 65 | $evm.log(:info, "Getting location id for '#{location}' from Satellite") 66 | location_id = query_id("locations", "name", location) 67 | raise "Cannot determine location id for '#{location}'" if location_id == -1 68 | $evm.log(:info, "location_id: #{location_id}") 69 | 70 | # 71 | # Get the organization id 72 | # 73 | $evm.log(:info, "Getting organization id for '#{organization}' from Satellite") 74 | organization_id = query_id("organizations", "name", organization) 75 | raise "Cannot determine organization id for '#{organization}'" if organization_id == -1 76 | $evm.log(:info, "organization_id: #{organization_id}") 77 | # 78 | # Create the host record 79 | # 80 | hostinfo = { 81 | :name => vm.name, 82 | :mac => vm.mac_addresses[0], 83 | :hostgroup_id => hostgroup_id, 84 | :location_id => location_id, 85 | :organization_id => organization_id, 86 | :build => 'false' 87 | } 88 | $evm.log(:info, "Creating host record in Satellite with the following details: #{hostinfo}") 89 | 90 | uri = "#{@uri_base}/hosts" 91 | request = RestClient::Request.new( 92 | method: :post, 93 | url: uri, 94 | headers: @headers, 95 | verify_ssl: OpenSSL::SSL::VERIFY_NONE, 96 | payload: { host: hostinfo }.to_json 97 | ) 98 | rest_result = request.execute 99 | $evm.log(:info, "return code => <#{rest_result.code}>") 100 | end 101 | end 102 | $evm.root['ae_result'] = 'ok' 103 | exit MIQ_OK 104 | rescue RestClient::Exception => err 105 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 106 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 107 | $evm.root['ae_result'] = 'error' 108 | $evm.root['ae_reason'] = "The REST request failed with code: #{err.response.code}" unless err.response.nil? 109 | exit MIQ_STOP 110 | rescue => err 111 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 112 | $evm.root['ae_result'] = 'error' 113 | $evm.root['ae_reason'] = "Error registering with Satellite: #{err.message}" 114 | exit MIQ_ERROR 115 | end 116 | -------------------------------------------------------------------------------- /chapter17/scripts/rename_service.rb: -------------------------------------------------------------------------------- 1 | begin 2 | # 3 | # Get the task object from root 4 | # 5 | service_template_provision_task = $evm.root['service_template_provision_task'] 6 | # 7 | # Get destination service object 8 | # 9 | service = service_template_provision_task.destination 10 | # 11 | # Get dialog options 12 | # 13 | dialog_options = service_template_provision_task.dialog_options 14 | # 15 | # Name the service 16 | # 17 | if dialog_options.has_key? 'dialog_service_name' 18 | service.name = "#{dialog_options['dialog_service_name']}" 19 | end 20 | if dialog_options.has_key? 'dialog_service_description' 21 | service.description = "#{dialog_options['dialog_service_description']}" 22 | end 23 | 24 | $evm.root['ae_result'] = 'ok' 25 | exit MIQ_OK 26 | rescue => err 27 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 28 | $evm.root['ae_result'] = 'error' 29 | $evm.root['ae_reason'] = "Error: #{err.message}" 30 | exit MIQ_ERROR 31 | end -------------------------------------------------------------------------------- /chapter17/scripts/set_configuration.rb: -------------------------------------------------------------------------------- 1 | require 'rest-client' 2 | require 'json' 3 | require 'openssl' 4 | require 'base64' 5 | 6 | begin 7 | 8 | def rest_action(uri, verb, payload=nil) 9 | headers = { 10 | :content_type => 'application/json', 11 | :accept => 'application/json;version=2', 12 | :authorization => "Basic #{Base64.strict_encode64("#{@username}:#{@password}")}" 13 | } 14 | response = RestClient::Request.new( 15 | :method => verb, 16 | :url => uri, 17 | :headers => headers, 18 | :payload => payload, 19 | verify_ssl: false 20 | ).execute 21 | return JSON.parse(response.to_str) 22 | end 23 | 24 | servername = $evm.object['servername'] 25 | @username = $evm.object['username'] 26 | @password = $evm.object.decrypt('password') 27 | 28 | if $evm.root['vmdb_object_type'] == 'miq_provision' 29 | parameter_id = $evm.root['miq_provision'].get_option(:dialog_parameter_id) 30 | parameter_value = $evm.root['miq_provision'].get_option(:dialog_parameter_value) 31 | hostgroup_id = $evm.root['miq_provision'].get_option(:dialog_hostgroup_id) 32 | hostname = $evm.root['miq_provision'].get_option(:dialog_vm_name) 33 | elsif $evm.root['vmdb_object_type'] == 'service_reconfigure_task' 34 | parameter_id = $evm.root['dialog_parameter_id'] 35 | parameter_value = $evm.root['dialog_parameter_value'] 36 | hostgroup_id = $evm.root['dialog_hostgroup_id'] 37 | hostname = $evm.root['dialog_vm_name'] 38 | end 39 | # 40 | # Only set the smart class parameter if we've been passed a parameter value from a service dialog 41 | # 42 | unless parameter_value.nil? 43 | uri_base = "https://#{servername}/api/v2" 44 | # 45 | # Get the domain name for the hostgroup 46 | # 47 | rest_return = rest_action("#{uri_base}/hostgroups/#{hostgroup_id}", :get) 48 | domain_name = rest_return['domain_name'] 49 | match = "fqdn=#{hostname}.#{domain_name}" 50 | # 51 | # See if the override match already exists 52 | # 53 | rest_return = rest_action("#{uri_base}/smart_class_parameters/#{parameter_id}/override_values", :get) 54 | override_value_id = 0 55 | if rest_return['total'] > 0 56 | rest_return['results'].each do |override_value| 57 | if override_value['match'] == match 58 | override_value_id = override_value['id'] 59 | end 60 | end 61 | end 62 | if override_value_id.zero? 63 | payload = { 64 | :match => match, 65 | :value => parameter_value 66 | } 67 | rest_return = rest_action("#{uri_base}/smart_class_parameters/#{parameter_id}/override_values", :post, JSON.generate(payload)) 68 | else 69 | payload = { 70 | :value => parameter_value 71 | } 72 | rest_return = rest_action("#{uri_base}/smart_class_parameters/#{parameter_id}/override_values/#{override_value_id}", :put, JSON.generate(payload)) 73 | end 74 | end 75 | 76 | $evm.root['ae_result'] = 'ok' 77 | exit MIQ_OK 78 | rescue RestClient::Exception => err 79 | $evm.log(:error, "The REST request failed with code: #{err.response.code}") unless err.response.nil? 80 | $evm.log(:error, "The response body was:\n#{err.response.body.inspect}") unless err.response.nil? 81 | $evm.root['ae_reason'] = "The REST request failed with code: #{err.response.code}" unless err.response.nil? 82 | $evm.root['ae_result'] = 'error' 83 | exit MIQ_STOP 84 | rescue => err 85 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 86 | $evm.root['ae_reason'] = "Unspecified error, see automation.log for backtrace" 87 | $evm.root['ae_result'] = 'error' 88 | exit MIQ_STOP 89 | end -------------------------------------------------------------------------------- /chapter17/state_machine.md: -------------------------------------------------------------------------------- 1 | ## The Service Provisioning State Machine 2 | 3 | The Service Provisioning State Machine (Class `ServiceProvision_Template`) controls the sequence of steps involved in provisioning the service. 4 | 5 | ![screenshot](images/screenshot8.png) 6 | 7 | The `ServiceProvision_Template` Class schema contains a number of States, as shown (illustrated is the `default` Instance of this State Machine): 8 |
9 | 10 | ![screenshot](images/screenshot4.png) 11 | 12 |
13 | As can be seen, most of the fields are **pre** and **post** placeholders around the main **provision** and **checkprovisioned** states, to allow for optional processing if required. The **configurechilddialog** state (by default commented out) can be used to populate the options[:dialog] hash in the child task if required. 14 | 15 | ### Passing Service Dialog Options to the Child and Grandchild Tasks 16 | 17 | One of the more complex tasks that must be achieved by some state in the Service Provisioning State Machine is to pass the values received from the Service Dialog (if there is one), to the actual Tasks performing the provisioning of the VM(s). The complexity arises from the multiple types of Task object that are involved in creating the Service, the Service Resources, and the actual VMs. 18 | 19 |

20 | ![task hierachy](images/task_hierarchy.png?) 21 |

22 | 23 | This object hierarchy is represented at the highest level by the Service Template Provisioning Task (which we access from ```$evm.root['service_template_provision_task']```). 24 | 25 | The Service Template Provisioning Task has an assocation, `.miq_request_tasks`, containing the _miq\_request\_task_ objects representing the creation of the _service resource(s)_, which are items or resources making up the service request (even a single service catalog item is treated as a bundle containing one service resource). 26 | 27 | Each _child_ (service resource) miq\_request\_task also has a `.miq_request_tasks` assocation containing the VM Provisioning Tasks associated with creating the actual VMs for the service resource. This _miq\_request\_task_ is provider-specific. 28 | 29 | It is to the second level of miq\_request\_task (also known as the _grandchild task_) that we must pass the Service Dialog values that affect the provisioning of the VM (such as `:vm_memory` or `:vm_target_name`). 30 | 31 | (see [Service Objects](service_objects.md) for more details of the service object structure) 32 | 33 | ### Accessing the Service Dialog Options 34 | 35 | If a service dialog has been used in the creation of an Automation request (either from a Button or from a Service), then the key/value pairs from the service dialog are added to the Request and subsequent Task objects both as individual keys accessible from `$evm.root`, and to the Task object's options hash as the `:dialog` key. 36 | 37 | ```ruby 38 | $evm.root['service_template_provision_task'].options[:dialog] = \ 39 | {"dialog_option_0_service_name"=>"New Server", \ 40 | "dialog_option_0_service_description"=>"My New Server", \ 41 | "dialog_option_0_vm_name"=>"rhel7srv023", \ 42 | "dialog_tag_0_department"=>"engineering", \ 43 | "request"=>"clone_to_service"} 44 | ``` 45 | 46 | or 47 | 48 | ```ruby 49 | $evm.root['dialog_option_0_service_description'] = My New Server 50 | $evm.root['dialog_option_0_service_name'] = New Server 51 | $evm.root['dialog_option_0_vm_name'] = rhel7srv023 52 | $evm.root['dialog_tag_0_department'] = engineering 53 | ``` 54 | 55 | Accessing the dialog options from `.options[:dialog]` is easier when we don't necessarily know the option name. 56 | 57 | When we have several generations of child _Task_ object (as we do when provisioning VMs from a service), we also need to pass the dialog options from the parent object (the Service Template Provision Task), to the various child objects, otherwise they won't be visible to the children. 58 | 59 | The key/value pairs from the service dialog can be inserted into the `.options[:dialog]` hash of a child Task object, using the `.set_dialog_option` method: 60 | 61 | ```ruby 62 | stp_task = $evm.root["service_template_provision_task"] 63 | vm_size = $evm.root['dialog_vm_size'] 64 | stp_task.miq_request_tasks.each do |child_task| 65 | case vm_size 66 | when "Small" 67 | memory_size = 4096 68 | when "Large" 69 | memory_size = 8192 70 | end 71 | child_task.set_dialog_option('dialog_memory', memory_size) 72 | end 73 | ``` 74 | 75 | This enables the child and grandchild VM Provision workflows (which run through the standard VM Provision State Machine that we have already studied) to access their own Task object `.options[:dialog]` hash, and set the custom provisioning options accordingly. 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /chapter17/tips_and_tricks.md: -------------------------------------------------------------------------------- 1 | ## Tips and Tricks 2 | 3 | There are a number of useful tips and tricks to be aware of when developing services. 4 | 5 | #### Test VM Provisioning First 6 | 7 | Before developing a service catalog item to provision a VM, test that an interactive provision (**Infrastructure -> Virtual Machines -> Lifecycle -> Provision VMs**) from the same VM Template, using the same VM settings, works successfully. 8 | 9 | This should include the same placement type (auto or manually selected), and the same CPU count and memory size ranges that will be offered from the service dialog. 10 | 11 | Troubleshooting a failing interactive VM provision is simpler than troubleshooting a failing service order. 12 | 13 | #### Re-Create the Service Item if the Template Changes 14 | 15 | If any changes are made to the Template that would result in a new internal Template ID, then the Service Catalog Item must be re-created (even if the new Template has the same name as the old). 16 | -------------------------------------------------------------------------------- /chapter17/working_with_services.md: -------------------------------------------------------------------------------- 1 | ## Working with Services 2 | 3 | We have seen that we can use CloudForms to easily provision VMs from the **Infrastructure -> Virtual Machines -> Lifecycle** or **Clouds -> Instances -> Lifecycle** button groups. This does however require the requester to supply values for all of the VM Provisioning Dialog options, for every provision request. 4 | 5 | CloudForms enables us to create Service Catalogs. These contain Catalog Items, and Bundles of Items, to enable users to provision one or more VMs (or other components) from a single **Order** button: 6 |

7 | 8 | ![screenshot](images/screenshot1.png) 9 |
10 | 11 | When we create a Service Catalog Item, we pre-select the VM Provisioning Dialog options, and optionally create a _Service Dialog_ to allow for user input when the service is ordered. In this way we can create pre-configured service definitions that suit our own use cases, for example, offering **small**, **medium** or **large** to specify VM sizes: 12 |

13 | 14 | ![screenshot](images/screenshot2.png) 15 |
16 | 17 | The remaining sections in this chapter discuss the details of creating and using Service Catalogs. 18 | 19 | -------------------------------------------------------------------------------- /chapter18/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot1.png -------------------------------------------------------------------------------- /chapter18/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot10.png -------------------------------------------------------------------------------- /chapter18/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot2.png -------------------------------------------------------------------------------- /chapter18/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot3.png -------------------------------------------------------------------------------- /chapter18/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot4.png -------------------------------------------------------------------------------- /chapter18/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot5.png -------------------------------------------------------------------------------- /chapter18/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot6.png -------------------------------------------------------------------------------- /chapter18/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot7.png -------------------------------------------------------------------------------- /chapter18/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot8.png -------------------------------------------------------------------------------- /chapter18/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter18/images/screenshot9.png -------------------------------------------------------------------------------- /chapter19/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot1.png -------------------------------------------------------------------------------- /chapter19/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot2.png -------------------------------------------------------------------------------- /chapter19/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot3.png -------------------------------------------------------------------------------- /chapter19/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot4.png -------------------------------------------------------------------------------- /chapter19/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot5.png -------------------------------------------------------------------------------- /chapter19/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter19/images/screenshot6.png -------------------------------------------------------------------------------- /chapter2/creating_the_class.md: -------------------------------------------------------------------------------- 1 | ##Creating the Class 2 | 3 | Before we create our first Automation script, we need to put some things in place. We'll begin by creating a new Domain called _ACME_. 4 | 5 | In the Automation Explorer, highlight the **Datastore** icon in the side bar, and click **Configuration -> Add a New Domain**: 6 |

7 | 8 | ![Screenshot](images/screenshot2.png) 9 | 10 |
11 | We'll give the Domain the name _ACME_, and ensure the **Enabled** checkbox is ticked: 12 |

13 | 14 | ![Screenshot](images/screenshot3.png?) 15 | 16 |
17 | Now we'll add a Namespace into this domain, called _General_. Highlight the _ACME_ Domain icon in the side bar, and click **Configuration -> Add a New Namespace**: 18 |

19 | 20 | ![Screenshot](images/screenshot4.png) 21 | 22 |
23 | Give the Namespace the name _General_: 24 |

25 | 26 | ![Screenshot](images/screenshot5.png) 27 | 28 |
29 | Now we'll create a new Class, called _Methods_. (it may seem that naming a class _Methods_ is somewhat confusing, however many of the generic Classes in the `ManageIQ` and `RedHat` Domains in the automation datastore are called `Methods` to signify their general-purpose nature). 30 | 31 | Highlight the _General_ Domain icon in the side bar, and click **Configuration -> Add a New Class*: 32 |

33 | 34 | ![Screenshot](images/screenshot6.png) 35 | 36 |
37 | Give the Class the name _Methods_: 38 |

39 | 40 | ![Screenshot](images/screenshot7.png) 41 | 42 |
43 | We'll leave the _Display Name_ blank for this example. 44 | 45 | We'll create a simple Schema. Click the **Schema** tab for the _Methods_ class, and click **Configuration -> Edit selected Schema**: 46 |

47 | 48 | ![Screenshot](images/screenshot8.png) 49 | 50 |
51 | Click **New Field**, and add a single field with name _execute_, **Type** _Method_ and **Data Type** _String_: 52 |

53 | 54 | ![Screenshot](images/screenshot9.png) 55 | 56 |
57 | Click the **tick** in the left hand column to save the field entry, and click the **Save** button to save the Schema. 58 |

59 | 60 | ![Screenshot](images/screenshot10.png) 61 | 62 |
63 | We now have our generic Class defininition called `Methods` setup, with a simple Schema that executes a single Method. 64 | 65 | -------------------------------------------------------------------------------- /chapter2/exit_status.md: -------------------------------------------------------------------------------- 1 | ## Exit Status Codes 2 | 3 | In our example we used an exit status code of MIQ_OK. Although with simple methods such as this we don't strictly need to specify an exit code, it's good practice to do so. When we build more advanced multi-Method Classes and State Machines, an exit code can signal an error condition to the Automation Engine so that action can be taken. 4 | 5 | There are four exit codes that we can use: 6 | 7 | **MIQ\_OK** (0) - Continues normal processing 8 | This is logged to `automation.log` as: 9 | 10 | ``` Method exited with rc=MIQ_OK 11 | ``` **MIQ\_WARN** (4) - Warning message, continues processing 12 | 13 | This is logged to `automation.log` as: 14 | 15 | ``` 16 | Method exited with rc=MIQ_WARN 17 | ``` **MIQ\_ERROR / MIQ\_STOP** (8) - Stops processing current object 18 | This is logged to `automation.log` as: 19 | 20 | ``` Stopping instantiation because [Method exited with rc=MIQ_STOP] 21 | ``` **MIQ\_ABORT** (16) - Aborts entire Automation instantiation 22 | This is logged to `automation.log` as: 23 | 24 | ``` Aborting instantiation because [Method exited with rc=MIQ_ABORT] 25 | ``` 26 | -------------------------------------------------------------------------------- /chapter2/hello_world.md: -------------------------------------------------------------------------------- 1 | ## Hello, World! 2 | 3 | Our first Automation method is very simple, we'll write an entry to the `automation.log` file. 4 | 5 | First we need to create an instance from our class. In the **Instances** tab of the new **Methods** Class, select **Configuration -> Add a New Instance**: 6 |

7 | 8 | ![Screenshot](images/screenshot11.png) 9 | 10 |
11 | We'll call the Instance _HelloWorld_, and it'll run (execute) a Method _hello\_world_: 12 |

13 | 14 | ![Screenshot](images/screenshot12.png) 15 | 16 |
Click the **Add** button. 17 | 18 | In the **Methods** tab of the new `Methods` Class, select **Configuration -> Add a New Method**: 19 |

20 | 21 | ![Screenshot](images/screenshot13.png) 22 | 23 |
24 | Name the Method _hello\_world_, and paste the following code into the **Data** window: 25 | 26 | ```ruby 27 | $evm.log(:info, "Hello, World!") 28 | exit MIQ_OK 29 | ``` 30 |
31 | 32 | ![Screenshot](images/screenshot14.png) 33 | 34 | Click the **Validate** button, and then the **Add** button. 35 | 36 | ### Running the Instance 37 | 38 | We'll run our new instance using the _Simulation_ functionality of Automation, but before that, ssh into the CloudForms appliance as _root_, and tail the `automation.log` file: 39 | 40 | ``` 41 | [root@cloudforms ~]# tail -f /var/www/miq/vmdb/log/automation.log 42 | ``` 43 | 44 | In the simulation we actually run an Instance called `Call_Instance` in the `/System/Request/` namespace of the `ManageIQ` domain, and this in turn calls our Instance using the _namespace_, _class_ and _instance_ attribute/value pairs that we pass to it (see [Ways of Entering Automation](../chapter10/ways_of_entering_automation.md)). 45 | 46 | From the **Automation -> Simulation** menu, complete the details in the **Options** sidebar as shown: 47 |

48 | 49 | ![Screenshot](images/screenshot15.png) 50 | 51 | 52 | (Leave the Object Attribute Type as _None_) 53 |

54 | 55 | ![Screenshot](images/screenshot16.png) 56 | 57 |
58 | ... then click **Submit** 59 | 60 | If all went well, we should see our "Hello, World!" message appear in the automation.log file. 61 | 62 | 63 | ``` 64 | ...INFO -- : Invoking [inline] method [/ACME/General/Methods/hello_world] with inputs [{}] 65 | ...INFO -- : Starting 66 | ...INFO -- : Hello, World! 67 | ...INFO -- : Ending 68 | ...INFO -- : Method exited with rc=MIQ_OK 69 | ``` 70 | Success! 71 | -------------------------------------------------------------------------------- /chapter2/images/circle.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/circle.graffle/data.plist -------------------------------------------------------------------------------- /chapter2/images/circle.graffle/image39.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/circle.graffle/image39.pdf -------------------------------------------------------------------------------- /chapter2/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot1.png -------------------------------------------------------------------------------- /chapter2/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot10.png -------------------------------------------------------------------------------- /chapter2/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot11.png -------------------------------------------------------------------------------- /chapter2/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot12.png -------------------------------------------------------------------------------- /chapter2/images/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot13.png -------------------------------------------------------------------------------- /chapter2/images/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot14.png -------------------------------------------------------------------------------- /chapter2/images/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot15.png -------------------------------------------------------------------------------- /chapter2/images/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot16.png -------------------------------------------------------------------------------- /chapter2/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot2.png -------------------------------------------------------------------------------- /chapter2/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot3.png -------------------------------------------------------------------------------- /chapter2/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot4.png -------------------------------------------------------------------------------- /chapter2/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot5.png -------------------------------------------------------------------------------- /chapter2/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot6.png -------------------------------------------------------------------------------- /chapter2/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot7.png -------------------------------------------------------------------------------- /chapter2/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot8.png -------------------------------------------------------------------------------- /chapter2/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter2/images/screenshot9.png -------------------------------------------------------------------------------- /chapter2/writing_and_running_our_own_scripts.md: -------------------------------------------------------------------------------- 1 | ## Writing & Running Our Own Automation Scripts 2 | 3 | 4 | Let's jump right in and start writing our first automation scripts. Before we do anything we need to ensure that the **Automation Engine** server role is ticked on one of the appliances in our CloudForms/ManageIQ Zone (many Automation actions are Zone-specific, so we may need to enable the role in several Zones). We do this from the **Configure -> Configuration** menu, selecting one of the CloudForms/ManageIQ Servers in the _Settings_ accordion. 5 |

6 | 7 | ![Screenshot](images/screenshot1.png) 8 | 9 | 10 | Note: Setting the **Automation Engine** role is necessary to be able to run _queued_ automation tasks (this includes anything that starts off as an Automation _Request_, which we'll cover later). Automation actions initiated directly from the WebUI - such as running Instances from Simulation, or processing methods to populate dynamic dialogs - are run on the UI appliance itself, regardless of whether it has the **Automation Engine** role enabled. 11 | 12 | -------------------------------------------------------------------------------- /chapter21/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot1.png -------------------------------------------------------------------------------- /chapter21/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot10.png -------------------------------------------------------------------------------- /chapter21/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot2.png -------------------------------------------------------------------------------- /chapter21/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot3.png -------------------------------------------------------------------------------- /chapter21/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot4.png -------------------------------------------------------------------------------- /chapter21/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot5.png -------------------------------------------------------------------------------- /chapter21/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot6.png -------------------------------------------------------------------------------- /chapter21/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot7.png -------------------------------------------------------------------------------- /chapter21/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot8.png -------------------------------------------------------------------------------- /chapter21/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter21/images/screenshot9.png -------------------------------------------------------------------------------- /chapter21/scripts/AutomationRequest_Pending.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Description: This method is launched from the not_approved method which raises the requst_pending event 3 | # when the provisioning request is NOT auto-approved 4 | # 5 | # Events: request_pending 6 | # 7 | # Model Notes: 8 | # 1. to_email_address - used to specify an email address in the case where the 9 | # requester does not have a valid email address.To specify more than one email 10 | # address separate email address with commas. (I.e. admin@company.com,user@company.com) 11 | # 2. from_email_address - used to specify an email address in the event the 12 | # requester replies to the email 13 | # 3. signature - used to stamp the email with a custom signature 14 | # 15 | 16 | ################################### 17 | # 18 | # Method: emailrequester 19 | # 20 | # Build email to requester with reason 21 | # 22 | ################################### 23 | def emailrequester(miq_request, appliance, msg) 24 | $evm.log('info', "Requester email logic starting") 25 | 26 | # Get requester object 27 | requester = miq_request.requester 28 | 29 | # Get requester email else set to nil 30 | requester_email = requester.email || nil 31 | 32 | # Get Owner Email else set to nil 33 | owner_email = miq_request.options[:owner_email] || nil 34 | $evm.log('info', "Requester email:<#{requester_email}> Owner Email:<#{owner_email}>") 35 | 36 | # if to is nil then use requester_email or owner_email 37 | to = nil 38 | to ||= requester_email # || owner_email 39 | 40 | # If to is still nil use to_email_address from model 41 | to ||= $evm.object['to_email_address'] 42 | 43 | # Get from_email_address from model unless specified below 44 | from = nil 45 | from ||= $evm.object['from_email_address'] 46 | 47 | # Get signature from model unless specified below 48 | signature = nil 49 | signature ||= $evm.object['signature'] 50 | 51 | # Set email subject 52 | subject = "Request ID #{miq_request.id} - Your Automation Request was not Auto-Approved" 53 | 54 | # Build email body 55 | body = "Hello, " 56 | body += "
#{msg}." 57 | body += "

Please review your Request and update or wait for approval from an Administrator." 58 | body += "

To view this Request go to: " 59 | body += "https://#{appliance}/miq_request/show/#{miq_request.id}" 60 | body += "

Thank you," 61 | body += "
#{signature}" 62 | 63 | # Send email to requester 64 | $evm.log('info', "Sending email to <#{to}> from <#{from}> subject: <#{subject}>") 65 | $evm.execute(:send_email, to, from, subject, body) 66 | end 67 | 68 | ################################### 69 | # 70 | # Method: emailapprover 71 | # 72 | # Build email to approver with reason 73 | # 74 | ################################### 75 | def emailapprover(miq_request, appliance, msg) 76 | $evm.log('info', "Approver email logic starting") 77 | 78 | # Get requester object 79 | requester = miq_request.requester 80 | 81 | # Get requester email else set to nil 82 | requester_email = requester.email || nil 83 | 84 | # Get Owner Email else set to nil 85 | owner_email = miq_request.options[:owner_email] || nil 86 | $evm.log('info', "Requester email:<#{requester_email}> Owner Email:<#{owner_email}>") 87 | 88 | # Override to email address below or get to_email_address from from model 89 | to = nil 90 | to ||= $evm.object['to_email_address'] 91 | 92 | # Override from_email_address below or get from_email_address from model 93 | from = nil 94 | from ||= $evm.object['from_email_address'] 95 | 96 | # Get signature from model unless specified below 97 | signature = nil 98 | signature ||= $evm.object['signature'] 99 | 100 | # Set email subject 101 | subject = "Request ID #{miq_request.id} - Automation request was not approved" 102 | 103 | 104 | # Build email body 105 | body = "Approver, " 106 | body += "
An automation request received from #{requester_email} is pending." 107 | body += "

#{msg}." 108 | body += "

For more information you can go to: https://#{appliance}/miq_request/show/#{miq_request.id}" 109 | body += "

Thank you," 110 | body += "
#{signature}" 111 | 112 | # Send email to approver 113 | $evm.log('info', "Sending email to <#{to}> from <#{from}> subject: <#{subject}>") 114 | $evm.execute(:send_email, to, from, subject, body) 115 | end 116 | 117 | # Get miq_request from root 118 | miq_request = $evm.root['miq_request'] 119 | raise "miq_request missing" if miq_request.nil? 120 | $evm.log('info', "Detected Request:<#{miq_request.id}> with Approval State:<#{miq_request.approval_state}>") 121 | 122 | # Override the default appliance IP Address below 123 | appliance = nil 124 | # appliance ||= 'evmserver.company.com' 125 | appliance ||= $evm.root['miq_server'].ipaddress 126 | 127 | # Get incoming message or set it to default if nil 128 | msg = miq_request.resource.message || "Request pending" 129 | 130 | # Email Requester 131 | emailrequester(miq_request, appliance, msg) 132 | 133 | # Email Approver 134 | emailapprover(miq_request, appliance, msg) 135 | 136 | -------------------------------------------------------------------------------- /chapter21/scripts/approve_request.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Description: This method is executed when the automation request is auto-approved 3 | # 4 | # Auto-Approve request 5 | $evm.log("info", "AUTO-APPROVING automation request") 6 | $evm.root["miq_request"].approve("admin", "Auto-Approved") -------------------------------------------------------------------------------- /chapter21/scripts/pending_request.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Description: This method is executed when the automation request is NOT auto-approved 3 | # 4 | # Get objects 5 | msg = $evm.object['reason'] 6 | $evm.log('info', "#{msg}") 7 | 8 | # Raise automation event: request_pending 9 | $evm.root["miq_request"].pending -------------------------------------------------------------------------------- /chapter21/scripts/validate_request.rb: -------------------------------------------------------------------------------- 1 | request = $evm.root['miq_request'] 2 | resource = request.resource 3 | raise "Automation Request not found" if request.nil? || resource.nil? 4 | 5 | $evm.log("info", "Checking for auto_approval") 6 | approval_type = $evm.object['approval_type'].downcase 7 | if approval_type == 'auto' 8 | $evm.root["miq_request"].approve("admin", "Auto-Approved") 9 | $evm.root['ae_result'] = 'ok' 10 | else 11 | msg = "Request was not auto-approved" 12 | resource.set_message(msg) 13 | $evm.root['ae_result'] = 'error' 14 | $evm.object['reason'] = msg 15 | end -------------------------------------------------------------------------------- /chapter22/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter22/images/screenshot1.png -------------------------------------------------------------------------------- /chapter23/images/miq_ci_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter23/images/miq_ci_workflow.png -------------------------------------------------------------------------------- /chapter23/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter23/images/screenshot1.png -------------------------------------------------------------------------------- /chapter23/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter23/images/screenshot2.png -------------------------------------------------------------------------------- /chapter23/miscellaneous_tips.md: -------------------------------------------------------------------------------- 1 | ## Miscellaneous Tips 2 | 3 | ### Updating the Appliance 4 | 5 | When a minor update to CloudForms Management Engine is released and installed (e.g. 5.4.1 -> 5.4.2), any changes to the Automate code are not automatically visible to the Automate Explorer. Go to **Import / Export**, and **Reset all Datastore custom classes and instances to default** to get the updates added and visible. 6 | 7 | ![Screenshot](images/screenshot2.png) 8 | 9 | ### The ManageIQ Coding Style and Standards Guide 10 | 11 | There is a ManageIQ Coding Style and Standards Guide [here](http://manageiq.org/documentation/development/coding_style_and_standards/), and a Ruby Style Guide [here](https://github.com/ManageIQ/ruby-style-guide). Although the guides don't specifically refer to Automation Coding style (it's more a guideline for ManageIQ code development), we can adopt the recommendations to keep our code clean and standards-compliant. 12 | 13 | The guides recommend naming Instances in _CamelCase_ and Methods in snake_case, i.e. 14 | 15 | ![Screenshot](images/screenshot1.png) 16 | 17 | ### Defensive Programming 18 | 19 | The dynamic nature of the object structure means that we have to be more careful about testing for **nil** conditions, testing whether hash keys exist before we access them, test whether variables are enumerable before we call .each on them, etc. 20 | 21 | Some examples are: 22 | 23 | ```ruby 24 | if this_object.respond_to?(:attributes) 25 | if this_object.attributes.respond_to? :each 26 | this_object.attributes.each do |key, value| 27 | ... 28 | ``` 29 | 30 | 31 | ```ruby 32 | user = $evm.root['user'] rescue nil 33 | unless user.nil? 34 | ... 35 | ``` 36 | 37 | ```ruby 38 | prov = $evm.root['miq_provision'] 39 | if prov.options.key?(:ws_values) 40 | ws_values = prov.options[:ws_values] 41 | ... 42 | ``` 43 | 44 | ### Use an External IDE 45 | 46 | The in-built WebUI code editor is fairly basic. It is often easier to develop in an external editor or IDE, and copy and paste code into the in-built editor when complete. 47 | 48 | 49 | ### Version Control 50 | 51 | There isn't any (yet). Git integration for the Automation Datastore is planned for a future release of ManageIQ/CloudForms. In the meantime, use a separate Git repository, but this is a manual process unfortunately. 52 | 53 | Several of Red Hat's US consultants including Cameron Wyatt and Alex Smith have created a really cool open source project for doing version control/Continuous Integration of CloudForms automate, dialogs, etc. across regions. 54 | 55 | This project provides a Continuous Integration (CI) pipeline for ManageIQ region data using Jenkins. It provides a pipeline view that allows you to visualize which version of region data (automate domains, dialogs, service catalogs, etc.) is in a region at a given time. 56 | 57 | ![Screenshot](images/miq_ci_workflow.png) 58 | 59 | See https://github.com/rhtconsulting/miq-ci for more information. 60 | -------------------------------------------------------------------------------- /chapter3/images/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot17.png -------------------------------------------------------------------------------- /chapter3/images/screenshot18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot18.png -------------------------------------------------------------------------------- /chapter3/images/screenshot19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot19.png -------------------------------------------------------------------------------- /chapter3/images/screenshot20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot20.png -------------------------------------------------------------------------------- /chapter3/images/screenshot21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot21.png -------------------------------------------------------------------------------- /chapter3/images/screenshot22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot22.png -------------------------------------------------------------------------------- /chapter3/images/screenshot23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter3/images/screenshot23.png -------------------------------------------------------------------------------- /chapter3/using_schema_object_variables.md: -------------------------------------------------------------------------------- 1 | ## Using Schema/Object Variables 2 | 3 | Our next Automation example will reference variables that we can define and set in the Schema of our Class and Instance. 4 | 5 | We'll edit the Schema of the `Methods` Class: 6 |

7 | 8 | ![Screenshot](images/screenshot17.png) 9 | 10 |
11 | We'll add three attributes, _servername_, _username_ and _password_, as shown: 12 |

13 | 14 | ![Screenshot](images/screenshot18.png) 15 | 16 |
17 | Click **Save**: 18 |

19 | 20 | ![Screenshot](images/screenshot19.png) 21 | 22 |
23 | We need to ensure that the Schema method (our _execute_ field) is listed _after_ the three new schema attributes in the field list, otherwise they won't be visible to the method when it runs. If necessary, run **Configuration -> Edit sequence** to shuffle the schema fields up or down: 24 |

25 | 26 | ![Screenshot](images/screenshot20.png) 27 | 28 |
29 | Now we'll create a new Instance as before, this time called _GetCredentials_, and we'll fill in some values for the _servername_, _username_ and _password_ Schema attributes: 30 |

31 | 32 | ![Screenshot](images/screenshot21.png) 33 | 34 |
35 | We'll create a Method _get\_credentials_ containing the following code: 36 | 37 | ```ruby 38 | $evm.log(:info, "get_credentials started") 39 | 40 | servername = $evm.object['servername'] 41 | username = $evm.object['username'] 42 | password = $evm.object.decrypt('password') 43 | 44 | $evm.log(:info, "Server: #{servername}, Username: #{username}, Password: #{password}") 45 | exit MIQ_OK 46 | ``` 47 |
48 | 49 | ![Screenshot](images/screenshot22.png) 50 | 51 |
52 | Finally we'll run the new Instance through **Automate -> Simulation** again, invoking `Call_Instance` once more with the following attributes: 53 |

54 | 55 | ![Screenshot](images/screenshot23.png) 56 | 57 |
58 | We check `automation.log`, and see that the attributes have been retrieved from the instance schema, and the password has been decrypted. 59 | 60 | ``` 61 | ...Invoking [inline] method [/ACME/General/Methods/get_credentials] with inputs [{}] 62 | ... Starting 63 | ... get_credentials started 64 | ... Server: myserver, Username: admin, Password: p@ssword 65 | ... Ending 66 | ...Method exited with rc=MIQ_OK 67 | ``` 68 | 69 | We can use this technique to securely store and retrieve credentials to connect to anything else in our Enterprise. 70 | -------------------------------------------------------------------------------- /chapter4/a_little_rails_knowledge.md: -------------------------------------------------------------------------------- 1 | ## A Little Rails Knowledge (goes a long way) 2 | CloudForms/ManageIQ is a Ruby on Rails application, and so the Automation Engine is created using Rails, but it cleverly hides most of the "Rails-ness" of the larger application from us. It is still useful however to have some understanding of a few of the features and concepts of Rails. 3 | 4 | ### Model-View-Controller 5 | Rails is a Model-View-Controller (MVC) application (see also [Ruby on Rails/Getting Started/Model-View-Controller](http://en.wikibooks.org/wiki/Ruby_on_Rails/Getting_Started/Model-View-Controller)) 6 |

7 | 8 | ![Screenshot 1](images/mvc.png?) 9 | 10 |
11 | The _Model_ is a represention of the underlying, logical structure of data from the database (which in the case of CloudForms/ManageIQ is PostgreSQL). When writing automation scripts, we work with models extensively (although we may not necessarily realise it). 12 | 13 | Rails Models are called _Active Records_. They always have a singular _CamelCase_ name (e.g. GuestApplication), and their corresponding database tables have a plural _snake\_case_ name (e.g. guest_applications). 14 | 15 | ### Active Record Associations 16 | 17 | Active Record Associations link the Models together in one-to-many and one-to-one relationships that allow us to traverse objects. 18 | 19 | We can illustrate this by looking at some of the Rails code that defines the _Host_ Active Record: 20 | 21 | ```ruby 22 | class Host < ActiveRecord::Base 23 | ... 24 | belongs_to :ext_management_system, :foreign_key => "ems_id" 25 | belongs_to :ems_cluster 26 | has_one :operating_system, :dependent => :destroy 27 | has_one :hardware, :dependent => :destroy 28 | has_many :vms_and_templates, :dependent => :nullify 29 | has_many :vms 30 | ... 31 | ``` 32 | We see that there are several associations from a host object, including to the cluster that it's a member of, and to the VMs that run on that host. 33 | 34 | Although these associations are defined in Rails, they are available to us when we work with the corresponding MiqAeService objects from the Automation Engine (see [The MiqAeService* Model](../chapter4/the_miqaeservice_model.md)). 35 | 36 | ### Rails Helper Methods (.find\_by\_*) 37 | Rails does a lot of things to make our lives easier, including dynamically creating _helper methods_. The most useful ones are the find\_by\_\* methods. 38 | 39 | ```ruby 40 | owner = $evm.vmdb('user').find_by_id( ownerid.to_i ) 41 | vm = $evm.vmdb('vm').find_by_name(vm_name) 42 | vm = $evm.vmdb('vm').find_by_guid(guid) 43 | ``` 44 | We can ```.find_by_``` any table heading on a database table, so if we look at the _services_ column: 45 | 46 | ``` 47 | vmdb_production=# \d services 48 | Table "public.services" 49 | Column | Type | Modifiers 50 | ----------------------+-----------------------------+------------------------------------------------------- 51 | id | bigint | not null default nextval('services_id_seq'::regclass) 52 | name | character varying(255) | 53 | description | character varying(255) | 54 | guid | character varying(255) | 55 | type | character varying(255) | 56 | service_template_id | bigint | 57 | options | text | 58 | display | boolean 59 | ... 60 | ``` 61 | 62 | We see that if we wanted we could call: 63 | 64 | ```ruby 65 | $evm.vmdb('service').find_by_description('My New Service') 66 | ``` 67 | 68 | Tip - don't try searching the CloudForms sources for ```def find_by_id``` though, these are not statically defined methods and so don't exist in the CloudForms code. The find_by helpers are also being deprecated in Rails in favour of a syntax using `where`, for example: `$evm.vmdb('service').where(:description => 'My New Service')` 69 | 70 | 71 | -------------------------------------------------------------------------------- /chapter4/distributed_ruby.md: -------------------------------------------------------------------------------- 1 | ## Distributed Ruby 2 | 3 | The Automation Engine runs in a CloudForms _worker_ thread, and it launches an Automation Method by spawning it as a child Ruby process. We can see this from the command line using **`ps`** to check the PID of the worker processes and its children: 4 | 5 | 6 | ``` 7 | \_ /var/www/miq/vmdb/lib/workers/bin/worker.rb 8 | | \_ /opt/rh/rh-ruby22/root/usr/bin/ruby <-- automation method running 9 | ``` 10 | 11 | The spawned Automation script must access the Service Model (MiqAeService*) objects that reside in the Automation Engine process, and it does this using Distributed Ruby. 12 | 13 | Distributed Ruby (dRuby) is a distributed client-server object system for Ruby. It uses a form of Remote Method Invocation to allow a client Ruby process to call methods on a Ruby object located in another (server) Ruby process, even on another machine. The object in the remote dRuby server process is locally represented in the dRuby client by an instance of a _DRb::DRbObject_ object. In the case of an Automation script, this object is our `$evm` variable. 14 | 15 | When the Automation Engine spawns an Automate Method it sets up the dRuby session automatically, and we access everything seamlesssly via `$evm` in our script. Behind the scenes however the dRuby libraries are shipping our calls over to the Automation Engine worker to handle the requests. 16 | 17 | Although this is mostly transparent to us, it can occasionally produce unexpected results. Perhaps we are hoping to find some useful user-related method that we can call on our user object, which we normally access as `$evm.root['user']`. We might try to call a standard Ruby method such as `$evm.root['user'].instance_methods`, but if so we actually get a list of the instance methods for the local _DRb::DRbObject_ object, rather than the remote MiqAeServiceUser service model (not what we want). 18 | 19 | When we get more adventurous in our scripting, we also occasionally get a _DRb::DRbUnknown_ object returned to us, indicating that our dRuby client doesn't know about the class definition for a distributed object. 20 | 21 | ### Examining CloudForms Workers 22 | 23 | We can use `rake` to see which workers are running on a CloudForms appliance, along with their status and Process IDs: 24 | 25 | ``` 26 | vmdb 27 | bin/rake evm:status 28 | 29 | ... 30 | Worker Type | Status | 31 | -------------------------------------------------------------------+---------+ 32 | ManageIQ::Providers::Redhat::InfraManager::EventCatcher | started | 33 | ManageIQ::Providers::Redhat::InfraManager::MetricsCollectorWorker | started | 34 | ManageIQ::Providers::Redhat::InfraManager::MetricsCollectorWorker | started | 35 | ManageIQ::Providers::Redhat::InfraManager::RefreshWorker | started | 36 | MiqEmsMetricsProcessorWorker | started | 37 | MiqEmsMetricsProcessorWorker | started | 38 | MiqEventHandler | started | 39 | MiqGenericWorker | started | 40 | MiqGenericWorker | started | 41 | MiqPriorityWorker | started | 42 | MiqPriorityWorker | started | 43 | MiqReportingWorker | started | 44 | MiqReportingWorker | started | 45 | MiqScheduleWorker | started | 46 | MiqSmartProxyWorker | started | 47 | MiqSmartProxyWorker | started | 48 | MiqUiWorker | started | 49 | MiqWebServiceWorker | started | 50 | ``` -------------------------------------------------------------------------------- /chapter4/images/general_mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter4/images/general_mvc.png -------------------------------------------------------------------------------- /chapter4/images/mvc.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter4/images/mvc.graffle/data.plist -------------------------------------------------------------------------------- /chapter4/images/mvc.graffle/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter4/images/mvc.graffle/image1.png -------------------------------------------------------------------------------- /chapter4/images/mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter4/images/mvc.png -------------------------------------------------------------------------------- /chapter4/some_background_theory.md: -------------------------------------------------------------------------------- 1 | ## Some Background Theory 2 | 3 | It can be useful to have an appreciation of Rails _models_, and how the Automation Engine encapsulates these as objects that we can program with. The objects represent the things that we are typically interested in when we write automation code, such as VMs, Clusters, Guest Applications, or even Provisioning Requests. 4 | 5 | The following three sections cover some of this background Rails and object model theory. They are useful sections, but can be skipped on first read if required. 6 | -------------------------------------------------------------------------------- /chapter5/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot1.png -------------------------------------------------------------------------------- /chapter5/images/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot10.png -------------------------------------------------------------------------------- /chapter5/images/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot11.png -------------------------------------------------------------------------------- /chapter5/images/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot12.png -------------------------------------------------------------------------------- /chapter5/images/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot13.png -------------------------------------------------------------------------------- /chapter5/images/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot14.png -------------------------------------------------------------------------------- /chapter5/images/screenshot15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot15.png -------------------------------------------------------------------------------- /chapter5/images/screenshot16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot16.png -------------------------------------------------------------------------------- /chapter5/images/screenshot17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot17.png -------------------------------------------------------------------------------- /chapter5/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot2.png -------------------------------------------------------------------------------- /chapter5/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot3.png -------------------------------------------------------------------------------- /chapter5/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot4.png -------------------------------------------------------------------------------- /chapter5/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot5.png -------------------------------------------------------------------------------- /chapter5/images/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot6.png -------------------------------------------------------------------------------- /chapter5/images/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot7.png -------------------------------------------------------------------------------- /chapter5/images/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot8.png -------------------------------------------------------------------------------- /chapter5/images/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter5/images/screenshot9.png -------------------------------------------------------------------------------- /chapter6/images/object_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/chapter6/images/object_model.png -------------------------------------------------------------------------------- /chapter8/enforce_antiaffinity_rules.md: -------------------------------------------------------------------------------- 1 | ## Example - Enforce Anti-Affinity Rules 2 | 3 | We can use the automation techniques that we've learnt so far to write a method to solve a realistic task. 4 | 5 | ### Task 6 | 7 | Write an Automation Method that enforces anti-affinity rules for VMs, based on a _server\_role_ tag applied to each VM. There should be only one VM of any _server\_role_ type running on any host in the cluster. 8 | 9 | The Automation Method should be run from a Button visible on the VM details page. If another VM with the same _server\_role_ tag is found running on the same host (hypervisor) as the displayed VM, then we live migrate the current VM to another host with no other such tagged VMs (VM migration is only supported when we're working with a VMware vCenter Provider). We also email all users in the EvmGroup-administrator group that the migration occurred. 10 | 11 | We can achieve the task using the following script (also available [here](https://github.com/pemcg/cloudforms-automation-howto-guide/blob/master/chapter8/scripts/enforce_anti_affinity.rb)): 12 |

13 | 14 | ```ruby 15 | #---------------------------------------------------------------------------- 16 | # 17 | # CFME Automate Method: enforce_anti-affinity 18 | # 19 | # Notes: This method enforces an anti-affinity rule based on server_role tag 20 | # 21 | #---------------------------------------------------------------------------- 22 | 23 | begin 24 | #---------------------------------------------------------------------------- 25 | def relocate_vm(vm) 26 | # 27 | # Get our host name 28 | # 29 | our_host = vm.host_name # <-- Virtual Column 30 | # 31 | # Loop through the other hosts in our cluster 32 | # 33 | target_host = nil 34 | vm.ems_cluster.hosts.each do |this_host| # <-- Two levels of Association 35 | next if this_host.name == our_host 36 | host_invalid = false 37 | this_host.vms.each do |this_vm| # <-- Association 38 | if this_vm.tags(:server_role).first == our_server_role 39 | host_invalid = true 40 | break 41 | end 42 | end 43 | next if host_invalid 44 | # 45 | # If we get to here then no duplicate server_role VMs have been found 46 | # on this host 47 | # 48 | target_host = this_host 49 | break 50 | end 51 | if target_host.nil? 52 | raise "No suitable Host found to migrate VM #{vm.name} to" 53 | else 54 | $evm.log(:info, "Migrating VM #{vm.name} to host: #{target_host.name}") 55 | # 56 | # Migrate the VM to this host 57 | # 58 | vm.migrate(target_host) # <-- Method 59 | end 60 | return target_host.name 61 | end 62 | #---------------------------------------------------------------------------- 63 | 64 | #---------------------------------------------------------------------------- 65 | def send_email(group_name, vm_name, new_host) 66 | # 67 | # Find the group passed to us, and pull out the user emails 68 | # 69 | recipients = [] 70 | group = $evm.vmdb('miq_group').find_by_description(group_name) 71 | group.users.each do |group_member| # <-- Association 72 | recipients << group_member.email # <-- Attribute 73 | end 74 | # 75 | # 'from' is the current logged-user who clicked the button 76 | # 77 | from = $evm.root['user'].email 78 | subject = "VM migration" 79 | body = "VM Name: #{vm_name} was live-migrated to Host: #{new_host}" 80 | body += " in accordance with anti-affinity rules" 81 | # 82 | # Send emails 83 | # 84 | recipients.each do |recipient| 85 | $evm.log(:info, "Sending email to <#{recipient}> from <#{from}> subject: <#{subject}>") 86 | $evm.execute(:send_email, recipient, from, subject, body) 87 | end 88 | end 89 | #---------------------------------------------------------------------------- 90 | 91 | 92 | #---------------------------------------------------------------------------- 93 | # Main code 94 | #---------------------------------------------------------------------------- 95 | # 96 | # We've been called from a button on the VM object, so we know that 97 | # $evm.root['vm'] will be loaded 98 | # 99 | vm = $evm.root['vm'] 100 | # 101 | # Find out this VM's server_role tag 102 | # 103 | our_server_role = vm.tags(:server_role).first 104 | $evm.log(:info, "VM #{vm.name} has a server_role tag of: #{our_server_role}") 105 | # 106 | # Loop through the other VMs on the same host 107 | # 108 | vm.host.vms.each do |this_vm| # <-- Two levels of Association 109 | next if this_vm.name == vm.name 110 | if this_vm.tags(:server_role).first == our_server_role 111 | $evm.log(:info, "VM #{this_vm.name} also has a server_role tag of: #{our_server_role}, taking remedial action") 112 | new_host = relocate_vm(vm) 113 | send_email('EvmGroup-administrator', vm.name, new_host) 114 | end 115 | end 116 | exit MIQ_OK 117 | 118 | rescue => err 119 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 120 | exit MIQ_STOP 121 | end 122 | ``` 123 | 124 | -------------------------------------------------------------------------------- /chapter8/scripts/enforce_anti_affinity.rb: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # 3 | # CFME Automate Method: enforce_anti-affinity 4 | # 5 | # Author: Peter McGowan (Red Hat) 6 | # 7 | # Notes: This method enforces an anti-affinity rule based on server_role tag 8 | # 9 | #---------------------------------------------------------------------------- 10 | 11 | begin 12 | #---------------------------------------------------------------------------- 13 | def relocate_vm(vm) 14 | # 15 | # Get our host name 16 | # 17 | our_host = vm.host_name 18 | # 19 | # Loop through the other hosts in our cluster 20 | # 21 | target_host = nil 22 | vm.ems_cluster.hosts.each do |this_host| 23 | next if this_host.name == our_host # Skip if this Host == our Host 24 | host_invalid = false 25 | this_host.vms.each do |this_vm| 26 | if this_vm.tags(:server_role).first == our_server_role 27 | host_invalid = true 28 | break 29 | end 30 | end 31 | next if host_invalid 32 | # 33 | # If we get to here then no duplicate server_role VMs have been found on this host 34 | # 35 | target_host = this_host 36 | break 37 | end 38 | if target_host.nil? 39 | raise "No suitable Host found to migrate VM #{vm.name} to" 40 | else 41 | $evm.log(:info, "Migrating VM #{vm.name} to host: #{target_host.name}") 42 | # 43 | # Migrate the VM to this host 44 | # 45 | vm.migrate(target_host) 46 | end 47 | return target_host.name 48 | end 49 | #---------------------------------------------------------------------------- 50 | 51 | #---------------------------------------------------------------------------- 52 | def send_email(group_name, vm_name, new_host) 53 | # 54 | # Find the group passed to us, and pull out the user emails 55 | # 56 | recipients = [] 57 | group = $evm.vmdb('miq_group').find_by_description(group_name) 58 | group.users.each do |group_member| 59 | recipients << group_member.email 60 | end 61 | # 62 | # 'from' is the current logged-user who clicked the button 63 | # 64 | from = $evm.root['user'].email 65 | subject = "VM migration" 66 | body = "VM Name: #{vm_name} was live-migrated to Host: #{new_host} in accordance with anti-affinity rules" 67 | # 68 | # Send emails 69 | # 70 | recipients.each do |recipient| 71 | $evm.log(:info, "Sending email to <#{recipient}> from <#{from}> subject: <#{subject}>") 72 | $evm.execute(:send_email, recipient, from, subject, body) 73 | end 74 | end 75 | #---------------------------------------------------------------------------- 76 | 77 | 78 | #---------------------------------------------------------------------------- 79 | # Main code 80 | #---------------------------------------------------------------------------- 81 | # 82 | # We've been called from a button on the VM object, so we know that 83 | # $evm.root['vm'] will be loaded 84 | # 85 | vm = $evm.root['vm'] 86 | # 87 | # Find out this VM's server_role tag 88 | # 89 | our_server_role = vm.tags(:server_role).first 90 | $evm.log(:info, "VM #{vm.name} has a server_role tag of: #{our_server_role}") 91 | # 92 | # Loop through the other VMs on the same host 93 | # 94 | vm.host.vms.each do |this_vm| 95 | next if this_vm.name == vm.name # Skip if this VM == our VM 96 | if this_vm.tags(:server_role).first == our_server_role 97 | $evm.log(:info, "VM #{this_vm.name} also has a server_role tag of: #{our_server_role}, taking remedial action") 98 | new_host = relocate_vm(vm) 99 | send_email('EvmGroup-administrator', vm.name, new_host) 100 | end 101 | end 102 | exit MIQ_OK 103 | 104 | rescue => err 105 | $evm.log(:error, "[#{err}]\n#{err.backtrace.join("\n")}") 106 | exit MIQ_STOP 107 | end 108 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemcg/manageiq-automation-howto-guide/0676499eb8be3a400ae4cad2d3a693314bbddf5a/cover.jpg --------------------------------------------------------------------------------