[openstack-dev] [Heat] HOT Software configuration proposal

Keith Bray keith.bray at RACKSPACE.COM
Mon Oct 28 23:14:49 UTC 2013


In-line comments.

On 10/28/13 5:43 PM, "Steve Baker" <sbaker at redhat.com> wrote:

>On 10/26/2013 05:25 AM, Clint Byrum wrote:
>> Excerpts from Angus Salkeld's message of 2013-10-24 18:48:16 -0700:
>>> On 24/10/13 11:54 +0200, Patrick Petit wrote:
>>>> Hi Clint,
>>>> Thank you! I have few replies/questions in-line.
>>>> Cheers,
>>>> Patrick
>>>> On 10/23/13 8:36 PM, Clint Byrum wrote:
>>>>> I think this fits into something that I want for optimizing
>>>>> os-collect-config as well (our in-instance Heat-aware agent). That is
>>>>> a way for us to wait for notification of changes to Metadata without
>>>>> polling.
>>>> Interesting... If I understand correctly that's kinda replacement of
>>>> cfn-hup... Do you have a blueprint pointer or something more
>>>> specific? While I see the benefits of it, in-instance notifications
>>>> is not really what we are looking for. We are looking for a
>>>> notification service that exposes an API whereby listeners can
>>>> register for Heat notifications. AWS Alarming / CloudFormation has
>>>> that. Why not Ceilometer / Heat? That would be extremely valuable for
>>>> those who build PaaS-like solutions above Heat. To say it bluntly,
>>>> I'd like to suggest we explore ways to integrate Heat with Marconi.
>>> Yeah, I am trying to do a PoC of this now. I'll let you know how
>>> it goes.
>>>
>>> I am trying to implement the following:
>>>
>>> heat_template_version: 2013-05-23
>>> parameters:
>>>    key_name:
>>>      type: String
>>>    flavor:
>>>      type: String
>>>      default: m1.small
>>>    image:
>>>      type: String
>>>      default: fedora-19-i386-heat-cfntools
>>> resources:
>>>    config_server:
>>>      type: OS::Marconi::QueueServer
>>>      properties:
>>>        image: {get_param: image}
>>>        flavor: {get_param: flavor}
>>>        key_name: {get_param: key_name}
>>>
>>>    configA:
>>>      type: OS::Heat::OrderedConfig
>>>      properties:
>>>        marconi_server: {get_attr: [config_server, url]}
>>>        hosted_on: {get_resource: serv1}
>>>        script: |
>>>          #!/bin/bash
>>>          logger "1. hello from marconi"
>>>
>>>    configB:
>>>      type: OS::Heat::OrderedConfig
>>>      properties:
>>>        marconi_server: {get_attr: [config_server, url]}
>>>        hosted_on: {get_resource: serv1}
>>>        depends_on: {get_resource: configA}
>>>        script: |
>>>          #!/bin/bash
>>>          logger "2. hello from marconi"
>>>
>>>    serv1:
>>>      type: OS::Nova::Server
>>>      properties:
>>>        image: {get_param: image}
>>>        flavor: {get_param: flavor}
>>>        key_name: {get_param: key_name}
>>>        user_data: |
>>>          #!/bin/sh
>>>          # poll <marconi url>/v1/queues/{hostname}/messages
>>>          # apply config
>>>          # post a response message with any outputs
>>>          # delete request message
>>>
>> If I may diverge this a bit, I'd like to consider the impact of
>> hosted_on on reusability in templates. hosted_on feels like an
>> anti-pattern, and I've never seen anything quite like it. It feels wrong
>> for a well contained component to then reach out and push itself onto
>> something else which has no mention of it.
>>
>> I'll rewrite your template as I envision it working:
>>
>> resources:
>>    config_server:
>>      type: OS::Marconi::QueueServer
>>      properties:
>>        image: {get_param: image}
>>        flavor: {get_param: flavor}
>>        key_name: {get_param: key_name}
>>
>>    configA:
>>      type: OS::Heat::OrderedConfig
>>      properties:
>>        marconi_server: {get_attr: [config_server, url]}
>>        script: |
>>          #!/bin/bash
>>          logger "1. hello from marconi"
>>
>>    configB:
>>      type: OS::Heat::OrderedConfig
>>      properties:
>>        marconi_server: {get_attr: [config_server, url]}
>>        depends_on: {get_resource: configA}
>>        script: |
>>          #!/bin/bash
>>          logger "2. hello from marconi"
>>
>>    serv1:
>>      type: OS::Nova::Server
>>      properties:
>>        image: {get_param: image}
>>        flavor: {get_param: flavor}
>>        key_name: {get_param: key_name}
>>        components:
>>          - configA
>>          - configB
>>        user_data: |
>>          #!/bin/sh
>>          # poll <marconi url>/v1/queues/{hostname}/messages
>>          # apply config
>>          # post a response message with any outputs
>>          # delete request message
>>
>> This only becomes obvious why it is important when you want to do this:
>>
>>     configC:
>>       type: OS::Heat::OrderedConfig
>>       properties:
>>         script: |
>>           #!/bin/bash
>>           logger "?. I can race with A, no dependency needed"
>>
>>     serv2:
>>       type: OS::Nova::Server
>>       properties:
>>       ...
>>       components:
>>         - configA
>>         - configC
>>
>> This is proper composition, where the caller defines the components, not
>> the callee. Now you can re-use configA with a different component in the
>> same template. As we get smarter we can have these configs separate from
>> the template and reusable across templates.
>>
>> Anyway, I'd like to see us stop talking about hosted_on, and if it has
>> been implemented, that it be deprecated and eventually removed, as it is
>> just plain confusing.
>>
>My components proposals had no hosted_on, but I've been thinking about
>the implications of implementing software configuration as resources,
>and one of the natural consequences might be that hosted_on is the best
>way of establishing the relationship with compute and its
>configurations. Let me elaborate.
>
>Lets say that Heat has resource types for software configuration, with
>the following behaviours:
>* like other resources, a config resource goes into CREATE IN_PROGRESS
>as soon as its dependencies are satisfied (these dependencies may be
>values signalled from other config resources)
>* a config resource goes to state CREATE COMPLETE when it receives a
>signal that configuration on the compute resource is complete (by some
>mechanism; wait condition, marconi message, whatevs)
>* the relationship between config resources and compute resources are
>achieved with existing intrinsic functions (get_resource, get_attr)
>
>This lifecycle behaviour means that a configuration resource can only
>run on a single compute resource, and that relationship needs to be
>established somehow. Config resources will have a quirk in that they
>need to provide 2 sources of configuration data at different times:
>1) cloud-init config-data (or other boot-only mechanism), which must be
>available when the compute resource goes into CREATE IN_PROGRESS
>2) the actual configuration data (oac metadata, puppet manifest, chef
>recipe) which the compute resource needs to be able to fetch and execute
>whenever it becomes available.
>
>The data in 1) implies that the compute needs to depend on the config,
>but then all concurrency is lost (this won't matter for a
>cloud-init-only config resource).  Either way, the data for 1) will need
>to be available when the config resource is still in state INIT
>COMPLETE, which may impose limitations on how that is defined (ie
>get_resource, get_attr not allowed).
>
>So, 2 concrete examples for handling config/compute dependencies:
>
>hosted_on
>---------
>resources:
>  configA
>    type: Heat::ScriptConfig
>    properties:
>       hosted_on:
>         get_resource: the_server
>       script: |
>         #!/bin/bash
>         logger "1. hello config A"
>  configB
>    type: Heat::ScriptConfig
>    properties:
>       hosted_on:
>         get_resource: the_server
>       script: |
>         #!/bin/bash
>         logger "1. hello config B"
>  the_server:
>    type: OS::Nova::Server
>
>Here, configA and configB go into CREATE IN_PROGRESS as soon as
>the_server is CREATE COMPLETE. configA and configB go into CREATE
>COMPLETE when heat-engine receives a signal that they are complete. This
>signal may include values that other resources depend on to start their
>creation.
>
>OS::Nova::Server config_resources
>---------------------------------
>resources:
>  configA
>    type: Heat::ScriptConfig
>    properties:
>       script: |
>         #!/bin/bash
>         logger "1. hello config A"
>  configB
>    type: Heat::ScriptConfig
>    properties:
>       script: |
>         #!/bin/bash
>         logger "1. hello config B"
>  the_server:
>    type: OS::Nova::Server
>    properties:
>      config_resources:
>        - {get_resource: configA}
>        - {get_resource: configB}
>
>Here there would need to be some bypassing of dependency calculation to
>allow configA and configB to be created after the_server, maybe by:
>* special treatment of config_resources to prevent dependencies being
>created
>* a get_resource variant which doesn't create a hard dependency
>(get_resource_deferred?)
>
>Neither the hosted_on nor the config_resources behaviours are ideal, but
>I'm leaning towards hosted_on at the moment since it doesn't require any
>new soft-dependency mechanism.

For what it's worth, I prefer the config_resources method, because I want
to reuse configs across multiple servers.  In the hosted_on
implementation, would there be a way to reuse configA on different servers
in the same stack?  The logical organization of what software to apply to
a server just seems to make more sense to me when I look at the syntax for
config_resources.  If I can specify multiple "hosted_on" servers for a
given config, then I could be ok with hosted_on, but still prefer
config_resources  for the reason that it seems logical to define what
software you want installed on a server than to define what servers you
want to install a piece of software on. Nit picky on my part for sure, but
food for thought.

>
>As for composability, what *actually* needs to be composable is the
>contents of the script (manifest, recipe) property. Everything else in a
>config resource is just stack-specific wiring.  There are a couple of
>ways this composability could be achieved:
>1) resource provider in the user environment which specifies the script
>property
>2) __include__ or some equivalent client-side inclusion mechanism

+1 to the __include__... This would be a useful feature to template
writers. What do you all think, should the include substitution logic live
in the python-heatclient so that Heat only ever receives the fully
composited template? (side note: I originally thought that stack based
resource providers worked as #includes... Turns out I was waaay wrong once
I understood them :-)

>
>With either of these options, intrinsic function calls need to be moved
>out of the script property and into a variables property. Any
>configuration management tool that supports variables passed as inputs
>can use them. If a given tool doesn't support variables then heat-engine
>could do the substitution when building the configuration data.
>
>Using resource providers has another interesting possibility. With a
>sufficiently advanced software-config base resource type, it should be
>possible to write a resource provider which adds support for a
>configuration management tool that heat otherwise knows nothing about.

A big thank you to all of you proposing ideas here and writing blueprints.
 This is good stuff!
-Keith




More information about the OpenStack-dev mailing list