Notebook: MacBook – Battery calibation
A few weeks ago, my apr'09 13" MacBook started behaving strangely. After a few minutes off the power cord, it would simply shutdown without warning (no display of the low battery warning message ... blam ! Black screen of death). The led battery indicators would show the battery to be at 80%, and if you started the laptop again, it would work for 5-10 minutes ... then blam !
I've looked for solutions to this issue for a while, this behaviour didn't match apple's documents about battery calibration / PMU reset in their knowledge base, i've tried following every steps documented there (links provided below) to no avail.
resetting the SMC
resetting the PRAM and NVRAM
I finally managed to get back to a properly running mac after following those instructions (Battery calibration) to the letter.
When the system would shutdown improperly, i would just start it up again until next time, and did this for 2 hours (about 6 or 7 power off / power on cycles) until the battery was completely depleted.
Then, i performed a full charge, allowing the system to rest for a few hours while on battery, powered it on, removed the battery adapter with my fingers crossed ... and ... I got my macbook back ! Woooot
scripting with chef
Chef is a very cool platform management solution. Once it is setup, you have a very clean solution to distribute server configurations across as many servers as you need, very cleanly (Ruby DSL) and quickly. A must on AWS.
One thing that you can't find easily in Chef's documentation is how to use the chef API to write scripts that would use the information chef is storing.
Imagine having a script that would run regularly (cron) and update your internal DNS zone with A records for each server it can find in your chef database.
I'm not sure I used the "right" approach. I just pasted some code i borrowed from knife (chef's command-line tool) and added it to my own script. I intend to find a cleaner approach later (mixins, inheritance, etc ...)
#!/usr/bin/env ruby require 'rubygems' require 'chef/application' require 'chef/client' require 'mixlib/cli' class Client < Chef::Application include Mixlib::CLI banner "Usage: #{$0} (options)" option :config_file, :short => "-c CONFIG", :long => "--config CONFIG", :description => "The configuration file to use" option :log_level, :short => "-l LEVEL", :long => "--log_level LEVEL", :description => "Set the log level (debug, info, warn, error, fatal)", :proc => lambda { |l| l.to_sym } option :log_location, :short => "-L LOGLOCATION", :long => "--logfile LOGLOCATION", :description => "Set the log file location, defaults to STDOUT", :proc => nil option :editor, :short => "-e EDITOR", :long => "--editor EDITOR", :description => "Set the editor to use for interactive commands", :default => ENV['EDITOR'] option :no_editor, :short => "-n", :long => "--no-editor", :description => "Do not open EDITOR, just accept the data as is", :boolean => true option :help, :short => "-h", :long => "--help", :description => "Show this message", :on => :tail, :boolean => true option :node_name, :short => "-u USER", :long => "--user USER", :description => "API Client Username" option :client_key, :short => "-k KEY", :long => "--key KEY", :description => "API Client Key" option :chef_server_url, :short => "-s URL", :long => "--server-url URL", :description => "Chef Server URL" option :yes, :short => "-y", :long => "--yes", :description => "Say yes to all prompts for confirmation" option :defaults, :long => "--defaults", :description => "Accept default values for all questions" option :print_after, :short => "-p", :long => "--print-after", :description => "Show the data after a destructive operation" option :format, :short => "-F FORMAT", :long => "--format FORMAT", :description => "Which format to use for output", :default => "json" option :version, :short => "-v", :long => "--version", :description => "Show chef version", :boolean => true, :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"}, :exit => 0 def configure_chef unless config[:config_file] full_path = Dir.pwd.split(File::SEPARATOR) (full_path.length - 1).downto(0) do |i| config_file_to_check = File.join([ full_path[0..i], ".chef", "client.rb" ].flatten) if File.exists?(config_file_to_check) config[:config_file] = config_file_to_check break end end # If we haven't set a config yet and $HOME is set, and the home # knife.rb exists, use it: if (!config[:config_file]) && ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'client.rb')) config[:config_file] = File.join(ENV['HOME'], '.chef', 'client.rb') end end # Don't try to load a knife.rb if it doesn't exist. if config[:config_file] Chef::Config.from_file(config[:config_file]) else # ...but do log a message if no config was found. self.msg("No knife configuration file found") end Chef::Config[:log_level] = config[:log_level] if config[:log_level] Chef::Config[:log_location] = config[:log_location] if config[:log_location] Chef::Config[:node_name] = config[:node_name] if config[:node_name] Chef::Config[:client_key] = config[:client_key] if config[:client_key] Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url] Mixlib::Log::Formatter.show_time = false Chef::Log.init(Chef::Config[:log_location]) Chef::Log.level(Chef::Config[:log_level]) Chef::Log.debug("Using configuration from #{config[:config_file]}") if Chef::Config[:node_name].nil? raise ArgumentError, "No user specified, pass via -u or specifiy 'node_name' in #{config[:config_file] ? config[:config_file] : "~/.chef/knife.rb"}" end end def parse_options(args=[]) super config end def run(args) parse_options(args) configure_chef Chef::Search::Query.new.search(:node,"*:*") do |n| ## iterate on every node. You have access to every information ## chef and ohai could collect from the nodes end end end Client.new.run
The command line options for your script are the same as knife's. And it works flawlessly.
notebook: running a unix command for a given amount of time
Sometimes, you need to run a command (capture, etc ...) for a given amount of time. Here's an example of a script capturing mysql traffic for half an hour :
#!/bin/sh DATE=`date +%Y%m%d%H%M%S` tcpdump -i eth0 port 3306 -s 65535 -x -n -q -tttt > capture-$DATE.out & sleep 1800 kill $!
notebook: nagios / ndo2db on centOS 5.5 64 bits
Trying to setup nagios 3.1 with ndo2db on a 64bits platform, ndo2bd may not work properly and crash over and over.
The symptoms are:
[1286679019] ndomod: Still unable to connect to data sink. 7575 items lost, 5000 queued items to flush.
in the nagios log file
and /var/log/messages
containing reports of ndo2db segfaults ...
Oct 10 12:52:26 ip-10-112-41-174 kernel: ndo2db[15666]: segfault at 00007fff8701cff8 rip 00002aaaabf2d211 rsp 00007fff8701d000 error 6
Apparently, this comes from ndo2db
being improperly linked to a 32bits version of the mysql client lib.
You need to configure ndo2db like this :
./configure --prefix=/opt/nagios --enable-mysql --disable-pgsql --with-ndo2db-user=nagios --with-ndo2db-group=nagios --with-mysql-lib=/usr/lib/mysql
then you can go on and install ndo2db as documented, nagios will happily log events into you supervision database
Some fun with groovy and AWS Identity And Access Management
I'm currently playing with the all new AWS identity and access management, and wanted to share some groovy magic to play with users and groups ...
@Grapes([ @Grab(group='com.amazonaws', module='aws-java-sdk', version='1.0.11') ]) import com.amazonaws.auth.BasicAWSCredentials import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient import com.amazonaws.services.identitymanagement.model.* AWS_ACCESS_KEY='MY AWESOME KEY' AWS_SECRET_KEY='MY EVEN MORE AWESOME KEY' def cred = new BasicAWSCredentials(AWS_ACCESS_KEY,AWS_SECRET_KEY) def ami = new AmazonIdentityManagementClient(cred) println "Group 'Administrators' ?" def admins = null try { admins = ami.getGroup(new GetGroupRequest().withGroupName('Administrators'))?.group } catch (NoSuchEntityException e) { println "Didn't find group 'Administrators' : creating it ..." admins = ami.createGroup(new CreateGroupRequest().withGroupName('Administrators')).group } println admins println "User 'erwan' ?" def erwan = null try { erwan = ami.getUser(new GetUserRequest().withUserName('erwan'))?.user } catch (NoSuchEntityException e) { println "Didn't find user 'erwan' : creating it ..." erwan = ami.createUser(new CreateUserRequest().withUserName('erwan')).user } println erwan if