Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Berksfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
site :opscode

metadata
cookbook "yum", "~> 2.0"
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
v1.1.0
------
* Add entity.rb to auto-select an existing entity by IP (Credit to Thomas Cate for this code)
* Add monitors.rb to configure monitors from a configuration hash

v1.0.1
------
* Switch to Fog gem from rackspace-monitoring gem. Fog 1.1.15 or higher is required.
Expand Down
66 changes: 65 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ python and python-pip (installed by this cookbook) for the raxmon-cli install
### General Requirements
* You need to either set the attributes for your Rackspace username and api key in attributes/default.rb or create an encrypted data bag per the following setup steps:

### Yum cookbook requirements

Due to a breaking upstream change opscode-cookbooks/yum must be below version 3.0.0.
Berkshelf version 3 will honor this, Berkshelf 2 may install the wrong version due to solver issues related to dependency cookbooks.
To work around this on Berkshelf 2 specify

```
cookbook "yum", "~> 2.0"
```

in the Berksfile of the cookbook you're running Berkshelf against.


## Setup

Take either step depending on your databag setup.
Expand Down Expand Up @@ -91,7 +104,58 @@ The following attributes are required, either in attributes/default.rb or an enc
* `node['cloud_monitoring']['rackspace_auth_region']`
* This must be set to either 'us' or 'uk', depending on where your account was created

# Usage
# Monitors Configuration Hash Usage

This cookbook contains a "monitors" recipe that abstracts the LWRPs away behind a configuration hash.
This recipe will handle generation of the entity, checks, and alarms.
It will search for existing entites by IP and use them via the entity recipe.
Please note that the cloud_monitoring::monitors recipe used in this example will always install the agent.

The following example configures CPU, load, disk, and filesystem monitors, with alarms enabled on the 5 minute load average:

```
#
# Configure monitors to suit our app
#

# Calculate default values
# Critical at x4 CPU count
cpu_critical_threshold = (node["cpu"]["total"] * 4)
# Warning at x2 CPU count
cpu_warning_threshold = (node["cpu"]["total"] * 2)

# Define our monitors
node.default['cloud_monitoring']['monitors'] = {
'cpu' => { 'type' => 'cpu', },
'load' => { 'type' => 'load_average',
'alarm' => {
'CRITICAL' => { 'conditional' => "metric['5m'] > #{cpu_critical_threshold}", },
'WARNING' => { 'conditional' => "metric['5m'] > #{cpu_warning_threshold}", },
},
},

'disk' => {
'type' => 'disk',
'details' => { 'target' => '/dev/xvda1'},
},
'root_filesystem' => {
'type' => 'filesystem',
'details' => { 'target' => '/'},
},
}

#
# Call the monitoring cookbook with our changes
#
include_recipe "cloud_monitoring::monitors"
```

The previous examples assume the following attributes are set in the role:
- ['cloud_monitoring']['rackspace_username']: API Username
- ['cloud_monitoring']['rackspace_api_key']: API Password
- ['cloud_monitoring']['notification_plan_id']: Notification plan ID for the alarms

# LWRP Usage

This cookbook exposes many different elements of the Cloud Monitoring product. We'll go over some examples and best
practices for using this cookbook. The most widely used parts of the system are the three core Resources in the system `Entity`, `Check` and `Alarm`. So we'll cover those first and tackle The other primitives towards the end.
Expand Down
10 changes: 9 additions & 1 deletion attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Cookbook Name:: cloud_monitoring
# Recipe:: default
#
# Copyright 2012, Rackspace
# Copyright 2014, Rackspace
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,3 +41,11 @@
default['cloud_monitoring']['credentials']['databag_name'] = 'rackspace'
default['cloud_monitoring']['credentials']['databag_item'] = 'cloud'

# Check default values
default['cloud_monitoring']['check_default']['period'] = 30
default['cloud_monitoring']['check_default']['timeout'] = 10

# Default main configuration hash for monitors.rb
# No checks are defined by default as there is an account-wide limit and each check incurrs billing
# http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/api-rsource-limits.html
default['cloud_monitoring']['monitors'] = {}
26 changes: 21 additions & 5 deletions libraries/cloud_monitoring.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@ module Opscode
module Rackspace
module Monitoring

def cm
def cm(rackspace_api_key = nil, rackspace_username = nil, rackspace_auth_url = nil)
# This is a simple helper method to deduplicate precedence logic code
def attribute_logic(argument, resource, databag)
# Precedence:
# 1) Arguments
# 2) new_resource variables (If available)
# 3) Data bag
if argument
return argument
end
if resource
return resource
end
return databag
end

begin
# Access the Rackspace Cloud encrypted data_bag
creds = Chef::EncryptedDataBagItem.load(
Expand All @@ -13,9 +28,10 @@ def cm
creds = {'username' => nil, 'apikey' => nil, 'auth_url' => nil }
end

apikey = new_resource.rackspace_api_key || creds['apikey']
username = new_resource.rackspace_username || creds['username']
auth_url = new_resource.rackspace_auth_url || creds['auth_url']
apikey = attribute_logic(rackspace_api_key, defined?(new_resource) ? new_resource.rackspace_api_key : nil, creds['apikey'])
username = attribute_logic(rackspace_username, defined?(new_resource) ? new_resource.rackspace_username : nil, creds['username'])
auth_url = attribute_logic(rackspace_auth_url, defined?(new_resource) ? new_resource.rackspace_auth_url : nil, creds['auth_url'])

Chef::Log.debug("Opscode::Rackspace::Monitoring.cm: creating new Fog connection") if(!defined?(@@cm) || @@cm.nil?)
@@cm ||= Fog::Rackspace::Monitoring.new(
:rackspace_api_key => apikey,
Expand Down Expand Up @@ -167,4 +183,4 @@ def get_token_by_label(label)
end
end
end
end
end
4 changes: 2 additions & 2 deletions metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
description "Installs/Configures Rackspace Cloud Monitoring"
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))

version "1.0.4"
version "1.1.0"

depends "apt", ">= 1.4.2"
depends "python"
depends "yum"
depends "yum", "~> 2.0"
depends "xml"

#chef_gem cookbook/library required for chef versions <= 10.12.0
Expand Down
53 changes: 53 additions & 0 deletions recipes/entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# Cookbook Name:: cloud_monitoring
# Recipe:: entity
#
# Configure the cloud_monitoring_entity LWRP to use the existing entity
# for the node by matching the server IP.
#
# Copyright 2014, Rackspace
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# cm is defined in libraries/cloud_monitoring.rb
class Chef::Recipe
include Opscode::Rackspace::Monitoring
end

cm(defined?(node['cloud_monitoring']['rackspace_api_key']) ? node['cloud_monitoring']['rackspace_api_key'] : nil,
defined?(node['cloud_monitoring']['rackspace_username']) ? node['cloud_monitoring']['rackspace_username'] : nil,
defined?(node['cloud_monitoring']['rackspace_auth_url']) ? node['cloud_monitoring']['rackspace_auth_url'] : nil)

response = cm.list_entities.body


response["values"].each do |value|
unless value["ip_addresses"].nil? || node["cloud"].nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have gone with something like next if value["ip_addresses"].nil? || node["cloud"].nil?, so that the next 3 lines aren't indented another level.

if value["ip_addresses"]["private0_v4"].eql? node["cloud"]["local_ipv4"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check the ["cloud"]["local_ipv4"] for nil? to avoid breaking this on non-cloud servers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Perhaps unless value["ip_addresses"].nil? || node["cloud"].nil? on L#33

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on this one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in bd55a4a

node.set['cloud_monitoring']['label'] = value["label"]
end
end
end

if node['cloud_monitoring']['label'].nil?
node.set['cloud_monitoring']['label'] = node.hostname
end

cloud_monitoring_entity node['cloud_monitoring']['label'] do
label node['cloud_monitoring']['label']
agent_id node['cloud_monitoring']['agent']['id']
rackspace_username node['cloud_monitoring']['rackspace_username']
rackspace_api_key node['cloud_monitoring']['rackspace_api_key']
action :create
end
53 changes: 53 additions & 0 deletions recipes/monitors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# Cookbook Name:: cloud_monitoring
# Recipe:: monitors
#
# Configure checks and alarms for Rackspace MaaS
#
# Copyright 2014, Rackspace
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Include dependency recipes
include_recipe "cloud_monitoring"
include_recipe "cloud_monitoring::agent"
include_recipe "cloud_monitoring::entity"

node['cloud_monitoring']['monitors'].each do |key, value|
cloud_monitoring_check key do
type "agent.#{value['type']}"
period value.has_key?('period') ? value['period'] : node['cloud_monitoring']['check_default']['period']
timeout value.has_key?('timeout') ? value['timeout'] : node['cloud_monitoring']['check_default']['timeout']
rackspace_username node['cloud_monitoring']['rackspace_username']
rackspace_api_key node['cloud_monitoring']['rackspace_api_key']
retries 2
details value.has_key?('details') ? value['details'] : nil
action :create
end

if value.has_key?('alarm')
value["alarm"].each do |alarm, alarm_value|
# TODO: Add customizable messages, abstract the conditional more, etcetera...
criteria = "if (#{alarm_value["conditional"]}) { return #{alarm}, '#{key} is past #{alarm} threshold' }"

cloud_monitoring_alarm "#{value['type']} #{alarm} alarm" do
check_label key
criteria criteria
notification_plan_id node['cloud_monitoring']['notification_plan_id']
action :create
end

end # alarm loop
end # has_key?('alarm')
end # monitors loop