├── .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 | 
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 | 
18 |
19 |
20 | ...and the field **Data Type** from a drop-down list:
21 |
22 |
23 | 
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 | 
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 | 
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 | 
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 | 
39 |
40 |
41 | 
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 | 
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 | 
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 | 
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 | 
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 | 
27 |
28 |
29 | We can clone the `/System/Event` namespace to our own domain, and add the corresponding Instance:
30 |
31 |
32 | 
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 | 
7 |
8 | For example `#event_type` might translate to **request\_created**:
9 |
10 |
11 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
--------------------------------------------------------------------------------
/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 | 
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 | 
27 |
28 | We change the second event\_handler line to trigger a **vm_start** policy event:
29 |
30 | 
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 | 
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 | 
34 |
35 |
36 | The `default` Instance created from this Class has the Method values filled in accordingly:
37 |
38 |
39 | 
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 | 
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 | 
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 | 
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 | 
7 |
8 |
9 | The Schema for the `ProvisionRequestApproval` State Machine is:
10 |
11 |
12 | 
13 |
14 |
15 | The `Default` Instance has the following Field values:
16 |
17 |
18 | 
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 | 
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 | 
13 |
14 |
15 | The `default` Instance is as follows:
16 |
17 |
18 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
10 |
11 | ###Schema
12 |
13 | The Provisioning Profile schema contains a number of Attributes, Relationships and Methods, as shown:
14 |
15 |
16 | 
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 | 
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 | 
6 |
7 | The `ManageIQ/System/CommonMethods/QuotaStateMachine/quota` State Machine Instance has the following Field values:
8 |
9 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
9 |
10 |
11 | The Schema for the `ServiceProvisionRequestApproval` State Machine is:
12 |
13 |
14 | 
15 |
16 |
17 | The `Default` Instance has the following Field values:
18 |
19 |
20 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
34 |
35 |
36 | From the `Integration` namespace:
37 |
38 | 
39 |
40 |
41 | From the `Service` namespace:
42 |
43 | 
44 |
45 |
46 | From the `System` namespace:
47 |
48 | 
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 | 
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 | 
76 |
77 | We complete the dialog and click **Submit**, and after a while our new Service is ready:
78 |
79 |
80 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
9 |
10 |
11 | We'll give the Domain the name _ACME_, and ensure the **Enabled** checkbox is ticked:
12 |
13 |
14 | 
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 | 
21 |
22 |
23 | Give the Namespace the name _General_:
24 |
25 |
26 | 
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 | 
35 |
36 |
37 | Give the Class the name _Methods_:
38 |
39 |
40 | 
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 | 
49 |
50 |
51 | Click **New Field**, and add a single field with name _execute_, **Type** _Method_ and **Data Type** _String_:
52 |
53 |
54 | 
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 | 
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 | 
9 |
10 |
11 | We'll call the Instance _HelloWorld_, and it'll run (execute) a Method _hello\_world_:
12 |
13 |
14 | 
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 | 
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 | 
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 | 
50 |
51 |
52 | (Leave the Object Attribute Type as _None_)
53 |
54 |
55 | 
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 | 
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 | 
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 | 
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 | 
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 | 
9 |
10 |
11 | We'll add three attributes, _servername_, _username_ and _password_, as shown:
12 |
13 |
14 | 
15 |
16 |
17 | Click **Save**:
18 |
19 |
20 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------