As we’ve seen, the most common way to provision a virtual machine is via the CloudForms WebUI (see Chapter 16). We click on Lifecycle → Provision VMs, complete the provisioning dialog, and a few minutes later our new virtual machine is ready.
There are times, however, when it is useful to be able to start the virtual machine provisioning process from an automation script, with no manual interaction. This then allows us to autoscale our virtual infrastructure, based on real-time or anticipated performance criteria. Perhaps we are an online retailer selling barbeques, for example. We could automatically monitor the short-range weather forecast via the API of a well-known weather website and scale out our online store servers if a period of fine weather is anticipated.
We can initiate the provisioning process programmatically by calling $evm.execute
to run the method create_provision_request
(see Chapter 7 for more information on these methods).
The create_provision_request
method takes a number of arguments, which correspond to the argument list for the original EVMProvisionRequestEx
SOAP API call. A typical call to provision a VM into RHEV might be:
# arg1 = version
args
=
[
'1.1'
]
# arg2 = templateFields
args
<<
{
'name'
=>
'rhel7-generic'
,
'request_type'
=>
'template'
}
# arg3 = vmFields
args
<<
{
'vm_name'
=>
'rhel7srv010'
,
'vlan'
=>
'public'
,
'vm_memory'
=>
'1024'
}
# arg4 = requester
args
<<
{
'owner_email'
=>
'[email protected]'
,
'owner_first_name'
=>
'Peter'
,
'owner_last_name'
=>
'McGowan'
}
# arg5 = tags
args
<<
nil
# arg6 = additionalValues (ws_values)
args
<<
{
'disk_size_gb'
=>
'50'
,
'mountpoint'
=>
'/opt'
}
# arg7 = emsCustomAttributes
args
<<
nil
# arg8 = miqCustomAttributes
args
<<
nil
request_id
=
$evm
.
execute
(
'create_provision_request'
,
*
args
)
The arguments to the create_provision_request
call are described next. The arguments match the fields in the provisioning dialog (and the values from the corresponding YAML template), and any arguments that are set to required: true
in the dialog YAML, but don’t have a :default:
value, should be specified. The exception for this is for subdependencies of other options; for example, if :provision_type:
is pxe
, then the suboption :pxe_image_id:
is mandatory. If the :provision_type:
value is anything else, then :pxe_image_id:
is not relevant.
In CloudForms versions prior to 4.0, the arguments were specified as a string, with each value separated by a pipe (|) symbol, like so:
"vm_name=rhel7srv010|vlan=public|vm_memory=1024"
With CloudForms 4.0, however, this syntax has been deprecated, and the options within each argument type should be defined as a hash as shown in the preceding example. This is more compatible with the equivalent RESTful API call to create a provisioning request.
The value for each hashed argument pair should always be a string; for example:
{
'number_of_vms'
=>
'4'
}
rather than:
{
'number_of_vms'
=>
4
}
The version
argument refers to the interface version. It should be set to 1.1.
The templateFields
argument denotes fields specifying the VM or template to use as the source for the provisioning operation. We supply a guid
or ems_guid
to protect against matching same-named templates on different providers within CloudForms Management Engine. The request_type
field should be set to one of: template
, clone_to_template
, or clone_to_vm
as appropriate. A normal VM provision from template is specified as:
'request_type'
=>
'template'
vmFields
allows for the setting of properties from the Catalog, Hardware, Network, Customize, and Schedule tabs in the provisioning dialog. Some of these are provider-specific, so when provisioning an OpenStack instance, for example, we need to specify the instance_type
, as follows:
# arg2 = vmFields
arg2
=
{
'number_of_vms'
=>
'3'
,
'instance_type'
=>
'1000000000007'
,
# m1.small
'vm_name'
=>
"
#{
$instance_name
}
"
,
'retirement_warn'
=>
"
#{
2
.
weeks
}
"
}
args
<<
arg2
The requester
argument allows for the setting of properties from the Request tab in the provisioning dialog. owner_email
, owner_first_name
, and owner_last_name
are required fields.
The tags
argument refers to tags to apply to the newly created VM—for example:
{
'server_role'
=>
'web_server'
,
'cost_center'
=>
'0011'
}
Additional values, also known as ws_values
, are name/value pairs stored with a provision request, but not used by the core provisioning code. These values are usually referenced from Automate methods for custom processing. They are added into the request options hash and can be retrieved as a hash from:
$evm
.
root
[
'miq_provision'
].
options
[
:ws_values
]
emsCustomAttributes
are custom attributes applied to the virtual machine through the provider as part of provisioning. Not all providers support this, although VMware does support native vCenter custom attributes, which if set are visible both in CloudForms and in the vSphere/vCenter UI.
miqCustomAttributes
are custom attributes applied to the virtual machine and stored in the CloudForms Management Engine database as part of provisioning. These VMDB-specific custom attributes are displayed on the VM details page (see Chapter 5 for an example of setting a custom attribute from a script).
The Rails code that implements the create_provision_request
call makes the assumption that any noninteractive provision request will use automatic placement, and it sets options[:placement_auto] = [true, 1]
as a request option. This also means, however, that it disregards any vmFields
options that we may set that are normally found under the Environment tab of an interactive provision request, such as cloud_tenant
or cloud_network
(these are hidden in the WebUI if we select Choose Automatically; see Figure 27-1).
If we try adding one of these (such as cloud_network
), we see in evm.log:
Unprocessed key <cloud_network> with value <"1000000000007">
The only way that we can set any of these placement options is to add them to the additionalValues/ws_values (arg6) argument list and then handle them ourselves in the CustomizeRequest
stage of the state machine.
For example, in our call to create_provision_request
we can set:
# arg6 = additionalValues (ws_values)
args
<<
{
'cloud_network'
=>
'10000000000031'
'cloud_tenant'
=>
'10000000000012'
}
We can then copy ManageIQ/Cloud/VM/Provisioning/StateMachines/Methods/openstack_CustomizeRequest into our own domain, and edit as follows:
#
# Description: Customize the OpenStack provisioning request
#
def
find_object_for
(
rsc_class
,
id_or_name
)
obj
=
$evm
.
vmdb
(
rsc_class
,
id_or_name
.
to_s
)
||
$evm
.
vmdb
(
rsc_class
)
.
find_by_name
(
id_or_name
.
to_s
)
$evm
.
log
(
:warn
,
"Couldn
'
t find an object of class
#{
rsc_class
}
with an ID or name matching
'
#{
id_or_name
}
'
"
)
if
obj
.
nil?
obj
end
# Get provisioning object
prov
=
$evm
.
root
[
"miq_provision"
]
ws_values
=
prov
.
options
.
fetch
(
:ws_values
,
{})
if
ws_values
.
has_key?
(
:cloud_network
)
cloud_network
=
find_object_for
(
'CloudNetwork'
,
ws_values
[
:cloud_network
]
)
prov
.
set_cloud_network
(
cloud_network
)
end
if
ws_values
.
has_key?
(
:cloud_tenant
)
cloud_tenant
=
find_object_for
(
'CloudTenant'
,
ws_values
[
:cloud_tenant
]
)
prov
.
set_cloud_tenant
(
cloud_tenant
)
end
$evm
.
log
(
"info"
,
"Provisioning ID:<
#{
prov
.
id
}
>
Provision Request ID:<
#{
prov
.
miq_provision_request
.
id
}
>
Provision Type: <
#{
prov
.
provision_type
}
>"
)
Being able to create provisioning requests programmatically gives us complete control over the process and has many uses. For example, when managing a scalable cloud application, we can configure a CloudForms alert to detect high CPU utilization on any of the existing cloud instances making up the workload. We could use the alert to send a management event that runs an Automate method to scale out the workload by provisioning additional instances (see Chapter 11).
We can also use create_provision_request
to create custom service catalog items, when the out-of-the-box service provisioning state machines do not provide the functionality that we need (see Chapter 39).
18.118.12.157