Author Archives: James F

Chef Windows Perf — Disabling Ohai Plugins

From Doug Ireton’s chefconf slides — here’s a code snippet for disabling underperforming or irrelevant Ohai plugins on your windows chef node.

Ohai::Config::disabled_plugins= [
 #
 # add to client.rb file -- c:\chef\client.rb
 #
 # ref: http://www.slideshare.net/opscode/chef-conf-windowsdougireton # slide 30
 # ohai plugins that have poor perf or are irrelevant to windows
 #
 "c", "cloud", "ec2", "rackspace", "eucalyptus", "command", "dmi", "dmi_common",
 "erlang", "groovy", "ip_scopes", "java", "keys", "lua", "mono", "network_listeners",
 "passwd", "perl", "php", "python", "ssh_host_key", "uptime", "virtualization",
 "windows::virtualization", "windows::kernel_devices"
 ]

The above snippet is also available here: https://gist.github.com/tcotav/7566353

Here’s a link to the relevant opscode docs on this: https://wiki.opscode.com/display/chef/Disabling+Ohai+Plugins

Idempotency, Chef, Powershell, Windows

Here are some notes on writing idempotent recipes in Chef using Powershell on Windows

make powershell fully functional as standalone script on windows first (and second, and third)
– are you sure you got the first thing done properly? y/n?

Here’s a crude skeleton for a base powershell script:

######################
try {
 $Some-Crazy-Command;
 $message = "We made it";
 $exitVal=2;
}
catch
{
 $message = "Join Error - ";
 # tack on the thrown error string here, $_
 $message += $_;
 $exitVal=1;
}
write-host $message;
exit $exitVal;
}
########################

Basically, you just want to pass along an exit status and message that chef will be able to key off.

You could easily add additional states with corresponding exit codes to the try block. Why do we do this? Well, we need to know if the script did what it was supposed to or not. In the example below, we want to know whether to restart the host after adding it to an AD domain.

So then, within the powershell script itself is where you want to manage your idempotency (or at least, it is more likely where you’re going to HAVE to manage it.

In the join AD example (assuming a new host — not a change… see below for notes on that), in the powershell script we would:

1) is the host already a member of an AD Domain?
2) if not, then join to a domain

* The case where it is a member of a different domain and you want to change it is more complicated as you would probably need to Remove-Computer and then reboot. On coming up, it would be a member of no domain, and on next chef run, would join new domain.

The powershell would look something like this:

function addComputer { param([string]$username, [string]$password, [string]$domain)
try {
 if ((gwmi win32_computersystem).partofdomain -eq $true) {
  # arguably here, I would check if it is the RIGHT domain... next rev...
  # $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
  # $domainName = $domain.name
  # < compare with passed in value >
  $message = \"The system is joined to the domain\";
  $exitVal=2;
 }
 else {
  add-computer -domain $domain -credential (New-Object System.Management.Automation.PSCredential   ($username, (ConvertTo-SecureString $password -AsPlainText -Force))) -passthru -verbose
  $message = \"computer joined to domain\";
  $exitVal=3;
 }
}
catch {
  $message = \"Join Error - \";
  $message += $_;
  $exitVal=1;
}
  write-host $message;
  exit $exitVal;
}

# this next line uses ruby
addComputer #{node['ad']['user']} #{node['ad']['pwd']} #{node['ad']['domain']}

here’s a gist of a more final (and better formatted) version of this: https://gist.github.com/tcotav/7489860

Now ANOTHER (potentially more *nix-y) way to do this is instead of a single monolithic script, you would just shell out for all the bits and then process the output in chef/ruby. The possible issue with going that way is that it would be more expensive resource (and probably time-wise) to continually spin up a powershell process to handle each command. This would be more relevant if you had 30 little cmdlets that you wanted to invoke.

Okay, you’ve looked at the gist and saw a line that made you wonder WTF?  It looked like this:

::Chef::Recipe.send(:include, Chef::Mixin::PowershellOut)

Well, we need that more than anything because we use it to capture the exit status and stdout/stderr of the powershell shellout.  There’s some debug log code in there to dump out these values (you know — for posterity).  That bit is:

result = powershell_out(script)
Chef::Log.debug("powershell exit #{result.exitstatus}")
Chef::Log.debug("powershell error #{result.stderr}")
Chef::Log.debug("powershell stdout #{result.stdout}")

This is just what it looks like.  We run the script (a variable brilliantly named “script” here).  The results go into… result.  Then from that object we access the 3 variables mentioned earlier.  Those then are what we use to pass the messages back OUT of the powershell process to Chef.

  # same as shell_out
  if result.exitstatus == 2
    Chef::Log.debug("Already part of domain: #{result.stdout}")
  elsif result.exitstatus == 3
    Chef::Log.debug("Joined domain: #{result.stdout}")
    # reboot if joining domain
    notifies :request, 'windows_reboot[5]', :delayed
  else
    Chef::Log.error("Domain join fail: #{result.stdout}")
    # any other actions here?  maybe flag the node?
  end

We don’t do much with the return, but we do something — we notify the windows reboot we CLEVERLY inserted earlier into our Chef recipe. What this line tells the recipe to do is to queue up a windows reboot AFTER the rest of the runlist for this host are done. In the context of our little example though, it shows how we would be able to interact and take action based on powershell runs.

And that’s about it for now.

Google Glass Mirror API Quickstart, Continued

This might be the most duh thing I’ve made publicly available (you should see the stuff I keep secret…), but when I reached the end of the Google provided Mirror API/Glass quickstart tutorial, I looked for the next page of explanation.  There was no next page.  What I was looking for was something that described the webapp just launched.  Maybe it isn’t necessary for anyone else, but it felt like an omission to me.  So, here’s my continuation of the quickstart tutorial docs…

(reference: https://developers.google.com/glass/quickstart/python)

Here’s a screenshot of the bottom section of the page that gets launched from the quickstart code:

Glass Sample Project Webapp

You may be looking at this image and thinking “well, that’s pretty self-explanatory given the h3’d titles over each section”, and you may be right.   That wasn’t the problem here though.  The problem was the gap between the launch of the webapp via appengine tool invocation and then a review of what a powerful tool this webapp is.

So, how does this help you?

This webapp allows you to exercise all of the Mirror API functionality without sullying yourself with any development. Of course, we’re in it for the development, so what we’re seeing is the result of the sample project.  Play with it a bit.  You can use the timeline tool to add items into your timeline.  Those items will (magically) appear in the timeline section across the top of the page (or in your Glass).  Once you get done fiddling around there, you can dig into the code.

First, open up /templates/index.html and walk through the html to see the invocations of the POST methods for the timeline form area:

<form action="/" method="post">
 <input type="hidden" name="operation" value="insertItem">
 <input type="hidden" name="message"
 value="A solar eclipse of Saturn. Earth is also in this photo. Can you find it?">
 <input type="hidden" name="imageUrl"
 value="/static/images/saturn-eclipse.jpg">
 <input type="hidden" name="contentType" value="image/jpeg">
<button type="submit">A picture
 <img src="/static/images/saturn-eclipse.jpg">
</button>
 </form>

Of note here is that the form POST’s to a single endpoint (“/”) and passes along the desired action as a parameter (hidden in the form, named “operation” in the form shown).  Okay, its still just a form — nothing bleeding edge there.

The python code that handles this routes all of the POST’s to /.  Let’s dig into the python code.  First, open up the main.py file.  This has most of its meat hidden away in subfiles, but what is done here is to register all of the routes that this webapp handles.  For example, the oath handler provides code for the routes  ‘/auth’ and ‘/oauth2callback’.

Next we’ll open up the main handler file /main_handler.py.  Look at the post method:

@util.auth_required
 def post(self):
   """Execute the request and render the template."""
   operation = self.request.get('operation')
   # Dict of operations to easily map keys to methods.
   operations = {
     'insertSubscription': self._insert_subscription,
     'deleteSubscription': self._delete_subscription,
     'insertItem': self._insert_item,
     'insertItemWithAction': self._insert_item_with_action,
     'insertItemAllUsers': self._insert_item_all_users,
     'insertContact': self._insert_contact,
     'deleteContact': self._delete_contact
   }
   if operation in operations:
     message = operations[operation]()

First up, you see the decorator there @util.auth_required. Open up the corresponding file, util.py, and check out the auth_required method there. The simple text translation of the code found there is: is user authorized? Yes, then carry on. No, forward request to /auth for authorization. This is in place so that all requests to the app will pass through authorization.

Back to post method source, you can see the map of the form’s “hidden” operations to actual method invocations in the source. These methods are then invoked at line 117,

message = operations[operation]() 

So if we posted a form that had the “operation” parameter with the value “insertItem”, this code would invoke it as

message=operations['insertItem']()

which would REALLY be, after the dict lookup:

message=self._insert_item()

From there, you can dig into how _insert_item() handles the data and interacts with the user timeline.  My notes appear in BLUE in the code below.

def _insert_item(self):
  """Insert a timeline item."""
  logging.info('Inserting timeline item')
  body = {
    'notification': {'level': 'DEFAULT'}
  }
  # self.request is the object holding the http form data POST'd
  if self.request.get('html') == 'on':
    body['html'] = [self.request.get('message')]
  else:
    body['text'] = self.request.get('message')
    media_link = self.request.get('imageUrl')
  # here we check if you passed along an image or if just a plain ol' insert
  if media_link:
    # lets go out and grab the bytes of your media so we can stuff 'em in timeline
    if media_link.startswith('/'):
      media_link = util.get_full_url(self, media_link)
    resp = urlfetch.fetch(media_link, deadline=20)
    media = MediaIoBaseUpload(
         io.BytesIO(resp.content), mimetype='image/jpeg', resumable=True)
  else:
    media = None

 # self.mirror_service is initialized in util.auth_required. <- existing comment
 # but important in that this does a LOT of stuff -- see below
 self.mirror_service.timeline().insert(body=body, media_body=media).execute()
 # this is the message spit back to webapp
 return 'A timeline item has been inserted.'

The one line

self.mirror_service.timeline().insert(body=body, media_body=media).execute()

does a number of things.  First, we grab the mirror_service that we initialized as part of our authorization.  From that mirror_service, we get an instance of a timeline.  Into that timeline, we build an insert object that contains the body that we built in our method (including any media that we sent along).  That might be a bit confusing as insert is a common verb used as a method name for putting objects into collections.  Here it is an object.  That’s why the last thing we have to do is call execute() on the insert object to have the data sent to the timeline.  (I didn’t dig into the code but I suspect that’s what happens there).

Next up — our first kinda app: a simple canned powerpoint presentation that you can push subscribers.

gnslngr.us business card