During the session today, I mentioned there's a proposal on the table.. but it 
exists in mostly some threads & some heads. This is an attempt to capture some 
of the mountain of items that make AttribInj.
You've been warned - TL;DR.

(note to reader - the CB2.0 references are WIP, and not intended to be 
presented as a done deal, even if the language occasionally reads that way)


What's the problem:
Chef is designed to be an eventually consistent system - clients will 
eventually get to the desired state. This typically works great.
There are 2 elements that make chef eventually consistent:

*         Failures. Chef deals with this by having idempotent resources. 
Consider a scenario where some resources succeed in changing the system state, 
and others fail. Since Chef-client stops on the first failure and reports it, 
all resources past the failure will not have been updated. However, on the next 
attempt, the successful resources are idempotent (won't attempt to redo), so 
they e.g. shouldn't needlessly restart services, or copy over existing files 
etc. The failed resource is attempted again. Once all the run-list items have 
they resources applied successfully, the node has converged.

*         Search. Behind the scenes, chef uses Solr, which is a full text 
search engine. This is in general a Good Thing, since it provides a very reach 
query capability. However, the full text search index takes quite a while to 
reflect changes in nodes (if you deploy a large cluster dedicated to search 
that might improve...but we're not google). Recipes that depend on query 
results for correct operation might work incorrectly, because of partial (or 
no) results being returned while the index is still being updated.

In general, eventual consistency is not a bad thing. But for an orchestration 
engine it is. Here's an example  (different than the meeting, since it's a bit 
simpler)
When deploying nova, it needs to identify the Mysql server and Keystone server. 
In a search based approach, the recipes will be performing a search for the 
nodes with the appropriate roles.  However, if the Solr index has yet to 
update, the searches might return 0 results. If the index does its thing for 
long enough to try and run chef-client quite a few times, Crowbar might decide 
that the deployment failed.

This is aggravated by Crowbar's ability to queue up multiple changes to the 
environment, and process the queue as quickly as possible. This leads to a 
situation that Mysql is deployed, followed quickly by Keystone, and when 
keystone is deployed, nova deployed is started. The speed of runs together with 
the large batches of changes in the cluster almost guarantee that the solr 
index has not yet kept up.

What's the problem part(b):
The Openstack deployment cookbooks/recipes developed by the Dell Crowbar team 
have been termed "crowbarized" by some folks. Here's a quick example of why:
Chef client natively assumes that nodes have an IP address, which matches what 
you'd get from EC2 for example. Crowbar supports a much richer set of 
networking capabilities that you might encounter when using real hw (e.g. 
multi-homed, bonded, vlans, bridges ). The Openstack components' recipes use 
this network richness by calling appropriate libraries to get the 'right' 
networking info - .e.g. the admin network IP address, vs the storage or public 
or nova_fixed, depending on context.
While this sounds great, it does present a challenge - access to this 
information is through a crowbar library injected into Chef, and the library 
looks up information in Node data structures that are maintained by Crowbar. 
... making the recipes somewhat tied to Crowbar.


The Solution(?):
Attribute injection attempts to offer an approach that can solve or at least 
alleviate these problems. Here's the general idea, then how it helps, and then 
some more details.

In general, each recipe will define a set of attributes that are required for 
it to run successfully. These attributes provide the information that either 
search or crowbar would have provided. The attributes can be populated in 
different ways, depending on the environment in which the recipes are used:

a)      With pure Chef - via the default attributes in the cookbook, the 
environments facility of Chef, or roles

b)      In Crowbar - crowbar will compute and inject override values for these 
attributes, at runtime, via chef  API's.

c)       In other deployment systems - in a manner either like a) or b) or 
telepathically.

How does this help?
For part a) of the problem - we can strive to avoid searches in recipes, that 
go against the slow Solr, and rather search against the Crowbar DB, which is 
boring old consistent SQL. This is somewhat natural, since crowbar assigns 
roles and configuration data to nodes, based on information for this DB.

For part b) - recipes/cookbooks now effectively have an API, that can be 
discussed. Different deployment systems can satisfy this API - i.e. provide 
values for these attributes, in their own way, without requiring changes to the 
underlying deployment code in the recipes.

The nitty gritty details:
(this is heavily based on Crowbar 1.0, with some tweaks).

Within crowbar there are 3 types of attributes that can be tied to a node:

1.       Node specific discovered information - this include ohai plugin 
attributes [2], and additional ones that crowbar writes to the node (e.g. what 
services are running on the node, what raid controllers are present etc).

2.       Node specific configuration - e.g. IP address, hostname, bios 
configuration.

3.       Configuration for a given barclamp's deployment - e.g for NTP, what's 
the upstream server.

Important but slightly different is

4.       A node's run-list - this is a per node list of roles and recipes that 
the chef-client execution will attempt to converge on the node. In a typical 
Chef deployment, this list is managed by users (or their tooling). Crowbar 
manages this list based on its state machine and deployment process.

All these values are stored in chef server, and acted upon by chef-client.

Access for reading:
When a chef client executes, all the values from above are ''flattened'' into a 
the nodes attributes, and are accessible as the "node hash". For more details, 
see[3]

Update ''lifecycle''
The attributes in 1. are handled by chef-client - whenever a chef client run 
completes, these are pushed to the chef server, overwriting what was there 
before.
Attribute types 2., 3. and 4. are managed by crowbar, and updated based on 
either user actions or internal state transitions within crowbar. As it might 
be obvious, those can't be stored on the node itself - there exists a race 
condition between crowbar updating these values, and a chef client attempting 
to run (or at least there was the potential for one in Crowbar1.0).
So, rather than being stored on the node, these attributes are represented in 
chef as roles - the node specific configuration (including the run-list) is in 
a role that applies to a single node, while the barclamp configuration role is 
assigned to all the nodes that are included in the configuration.


Changes for CB2.0
Much of the above is the same as CB1.0. What is different?


1)      Default attributes as API, based on patterns agreed/discussed with 
wider openstack community (or pilfered from other folks who've open-sourced 
their cookbooks). A follow up email will describe the patterns we discussed 
during the Jan/22 hack day [4] and with the general community.

2)      Search result injection - there are a few options here,

a.       Postpone this, since we're short on time, and things to eventually 
work - just retry forever if needed, but be explicit about reporting status 
(and option to cancle). Keep recipes using search where currently used.

b.      The Jig/Job orchestration engine allows barclamps to define custom 
"Jobs" that are executed as the proposal is rolled out (see below).

Some background about jobs, and how search can be handled (since there wasn't a 
whole lot of ML activity around jobs):
When deploying a snapshot, there are many required steps - from allocating the 
node, deploying each role in the right element order to the subset of nodes on 
which it applies, then deploying the next set of roles. A simple example might 
be Nova, when a user commits a snapshot for deployment:

*         The nodes that are used in the proposal are automatically allocated, 
if they're not already

*         The nova controller is deployed first

*         Once the controller is deployed (which also creates the MySql schea), 
the nova-compute and nova-volumes (if any) are deployed.

Each one of these steps is a job (one per node x step), that together form a 
DAG (persisted in the DB).  A job can be crowbar framework provided logic (e.g. 
allocate node), or barclamp specific logic.  When events occur in the system (a 
node transitions state, a chef-client run finishes), the set of pending jobs 
are evaluated - all the jobs who's pre-requisites met are 'performed' (the 
'perform' method is executed on them).


Back to search:
There are 2 cases for search, via (partial) examples:

a)      Nova - nova needs to locate the keystone server. This is and inter 
barclamp dependency, and affects not just search, but order of deployment as 
well (nova proposal won't be deployed until the referenced keystone one is).

b)      Swift - when running chef-client on the ring-compute node there's a 
need to find all the disks on all the storage nodes that have already been 
deployed. This is intra-barclamp search, and dependency - the deployment of the 
ring-compute role will only start once the storage nodes are deployed.


Note the connection between search and dependencies ;) (job represent 
dependencies).
Both cases can handled very similarly, with the only difference is the source 
of dependency information, and the granularity of dependency.

In case a) the nova barclamp (taken from the cb1.0 NovasService < 
ServiceObject) implements something like:
  def proposal_dependencies(role)
    answer = []
    answer << { "barclamp" => "mysql", "inst" => 
role.default_attributes["nova"]["db"]["mysql_instance"] }
    answer << { "barclamp" => "keystone", "inst" => 
role.default_attributes["nova"]["keystone_instance"] }
    answer << { "barclamp" => "glance", "inst" => 
role.default_attributes["nova"]["glance_instance"] }
    answer
  end
The above represents that the proposal depends on the respective instances of 
mysql, keystone and glance (instance == deployment name in cb2.0 speak).
The Jig engine will ensure (via a JobWaitForSnapshotDeployment injected into 
the DAG) that before nova-controller starts deployment, these dependencies have 
been met. The above job identifies (via the type/key fields) the deployment 
it's waiting on. It's marked as done when a deployment of a whole snapshot is 
completed, triggering the re-evaluation of the nova jobs.

In case b) (swift storage nodes) - the dependency is source is from the run 
order defined by the swift barclamp (bc-template-swift.json). The dependency in 
this case is that all the nodes to whom the storage-node has been assigned, 
have finished their deployment of this role (i.e. just a single role from 
within the barclamp, not the whole snapshot).
The job for this is JobWaitForRoleOnNode. When a chef-client run completes, all 
pending jobs waiting for a role are evaluated, and those that match are marked 
as done.


Ok, your eyes are bleeding and my fingers ache. Hope this is somewhat sane, and 
is at least a good enough start for the convo to follow.




[1] attribute precedence: 
http://docs.opscode.com/essentials_cookbook_attribute_files_attribute_precedence.html
[2] ohai attributes: http://docs.opscode.com/ohai.html
[3] computing attributes 
http://docs.opscode.com/breaking_changes_chef_11.html#computing-attributes-from-attributes
[4] Chef for OpenStack Hack Day - Boston 
http://www.eventbrite.com/event/4395344594








_______________________________________________
Crowbar mailing list
Crowbar@dell.com
https://lists.us.dell.com/mailman/listinfo/crowbar
For more information: http://crowbar.github.com/

Reply via email to