cloud66_agent 0.0.1.pre2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloud66_agent.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Cloud 66
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Cloud66
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cloud66_agent'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cloud66_agent
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/cloud66_agent ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'logger'
4
+ require 'cloud66_agent/utils/config'
5
+ require 'cloud66_agent/utils/version'
6
+ require 'cloud66_agent'
7
+
8
+ # global config variable
9
+ $config = Cloud66::Utils::Config.new
10
+
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Cloud 66 Agent v#{Cloud66::Utils::Version.current}"
13
+
14
+ # following opts only for registration
15
+ opts.on('--url URL', 'Server URL') do |v|
16
+ $config.url = v
17
+ end
18
+ opts.on('--sockets SOCKETS', 'Sockets URL') do |v|
19
+ $config.faye_url = v
20
+ end
21
+ opts.on('--api-key APIKEY', 'API key') do |v|
22
+ $config.api_key = v
23
+ end
24
+ opts.on('--secret-key SECRETKEY', 'Secret Key') do |v|
25
+ $config.secret_key = v
26
+ end
27
+ opts.on('--server SERVERUID', 'Server id') do |v|
28
+ @server_uid = v
29
+ end
30
+
31
+ # respond to version requests
32
+ opts.on('--version') do |v|
33
+ puts "Cloud 66 Agent #{Cloud66::Utils::Version.current}"
34
+ exit 0
35
+ end
36
+
37
+ # logging configuration
38
+ opts.on('--log LOG', 'Log file path') do |v|
39
+ v == "STDOUT" ? $config.log_path = STDOUT : $config.log_path = v
40
+ end
41
+ opts.on('--log_level LOGLEVEL', 'Log level (int)') do |v|
42
+ $config.log_level = v.to_i
43
+ end
44
+ end.parse!
45
+
46
+ # prepare the global logger
47
+ $logger = Logger.new($config.log_path)
48
+ $logger.level = $config.log_level
49
+
50
+ if $config.disabled
51
+ # no other commands allowed while disabled
52
+ $logger.error "This agent had been disabled. Please contact Cloud 66 if you think this is in error"
53
+ exit -1
54
+ end
55
+
56
+ #pick up the command used
57
+ command = ARGV[0].downcase unless ARGV[0].nil?
58
+ if command.nil? || command.empty?
59
+ $logger.debug("Cloud 66 Agent v#{Cloud66::Utils::Version.current} (#{$config.is_agent_configured? ? 'Configured' : 'Not Configured'})")
60
+ exit 0
61
+ end
62
+
63
+ if !$config.is_agent_configured? && command != 'configure'
64
+ # no other commands allowed while not configured
65
+ $logger.error "Can only do command \"configure\" (until its been configured!)"
66
+ exit -1
67
+ end
68
+
69
+ # handle commands
70
+ $logger.debug "Performing: \"#{command}\""
71
+ if command == "configure"
72
+ Cloud66Agent.configure @server_uid
73
+ elsif Cloud66Agent.respond_to? command
74
+ Cloud66Agent.send command
75
+ else
76
+ $logger.error "Invalid command: #{command}"
77
+ exit -1
78
+ end
79
+ exit 0
80
+
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloud66_agent/utils/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cloud66_agent"
8
+ gem.version = Cloud66::Utils::Version.current
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.date = '2013-04-02'
11
+ gem.authors = ["Cloud 66"]
12
+ gem.email = ['hello@cloud66.com']
13
+ gem.description = "See https://cloud66.com for more info"
14
+ gem.summary = "Cloud 66 server component"
15
+ gem.homepage = 'http://cloud66.com'
16
+ # Manifest
17
+ gem.files = `git ls-files`.split("\n")
18
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
20
+ gem.require_paths = ["lib"]
21
+ # dependencies
22
+ gem.add_dependency('httparty', '>= 0.8.1')
23
+ gem.add_dependency('sys-filesystem', '~>1.0.0')
24
+ end
25
+
26
+
27
+ # gem.add_dependency('json', '>= 1.6.3')
28
+ # gem.add_dependency('eventmachine', '>=0.12.0')
29
+ # gem.add_dependency('faye', '>=0.8.0')
30
+ # gem.add_dependency('open4', '>=1.3.0')
31
+ # gem.add_dependency('fog', '~>1.4.0')
32
+ # gem.add_dependency('cloud66-backup', '~>3.0.25')
33
+ # gem.add_dependency('highline', '~>1.6.11')
34
+
35
+
@@ -0,0 +1,21 @@
1
+ require 'cloud66_agent/utils/vital_signs'
2
+ require 'cloud66_agent/utils/server'
3
+
4
+ module Cloud66
5
+ module Commands
6
+ class Address
7
+ def self.perform
8
+ begin
9
+ data = Utils::VitalSigns.address_info
10
+ rescue => exc
11
+ data = { error: exc.message }
12
+ end
13
+ Utils::Server.send_address data
14
+ exit 0
15
+ rescue => exc
16
+ $logger.error "Address Failed: #{exc.message}"
17
+ exit -1
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ require 'cloud66_agent/utils/vital_signs'
2
+ require 'cloud66_agent/utils/version'
3
+ require 'cloud66_agent/utils/server'
4
+
5
+ module Cloud66
6
+ module Commands
7
+ class Configure
8
+ def self.perform(server_uid)
9
+ begin
10
+ address_info = Utils::VitalSigns.address_info
11
+ data = {
12
+ :timezone => Time.new.zone,
13
+ :server_uid => server_uid,
14
+ :ext_ip4 => address_info[:ext_ip4],
15
+ :int_ip4 => address_info[:int_ip4],
16
+ :ext_ip6 => address_info[:ext_ip6],
17
+ :int_ip6 => address_info[:int_ip6],
18
+ :version => Utils::Version.current,
19
+ :system => Utils::VitalSigns.system_info }
20
+ rescue => exc
21
+ data = { error: exc.message }
22
+ end
23
+ result = Utils::Server.send_configure data
24
+ $config.agent_uid = result['uid']
25
+ $config.save
26
+ exit 0
27
+ rescue => exc
28
+ $logger.error "Configure Failed: #{exc.message}"
29
+ exit -1
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require 'cloud66_agent/utils/vital_signs'
2
+ require 'cloud66_agent/utils/server'
3
+
4
+ module Cloud66
5
+ module Commands
6
+ class Pulse
7
+ def self.perform
8
+ Utils::Server.send_pulse
9
+ exit 0
10
+ rescue => exc
11
+ $logger.error "Pulse Failed: #{exc.message}"
12
+ exit -1
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'cloud66_agent/utils/vital_signs'
2
+ require 'cloud66_agent/utils/server'
3
+
4
+ module Cloud66
5
+ module Commands
6
+ class Vitals
7
+ def self.perform
8
+ begin
9
+ data = Utils::VitalSigns.vitals_info
10
+ rescue => exc
11
+ data = { error: exc.message }
12
+ end
13
+ Utils::Server.send_vitals data
14
+ exit 0
15
+ rescue => exc
16
+ $logger.error "Vitals Failed: #{exc.message}"
17
+ exit -1
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,92 @@
1
+ require 'cloud66_agent/server'
2
+ require 'cloud66_agent/utils/vital_signs'
3
+ require 'cloud66_agent/utils/version'
4
+
5
+ module Cloud66
6
+ module Plugins
7
+ class Backup < Plugin
8
+ def self.perform(server_uid)
9
+ rescue => exc
10
+ $logger.error exc
11
+ exit -1
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ #require File.join(File.dirname(__FILE__), 'quartz_plugin')
20
+ #require 'tempfile'
21
+ #require 'fileutils'
22
+ #
23
+ #class Backup < QuartzPlugin
24
+ #
25
+ # @@version_major = 0
26
+ # @@version_minor = 0
27
+ # @@version_revision = 1
28
+ #
29
+ # def info
30
+ # { :uid => "64cec894b98b43ce9e6047a9284a4b7d", :name => "Backup", :version => get_version }
31
+ # end
32
+ #
33
+ # def run(message)
34
+ #
35
+ # pl = payload(message)
36
+ #
37
+ # job_name = pl['job_name']
38
+ # backup_script = "#Script generated by Cloud66 job '#{job_name}'\n\n" + pl['script']
39
+ # script_name = pl['script_name']
40
+ #
41
+ # random_file = Tempfile.new('cloud66').path + '.rb'
42
+ # File.open(random_file, 'w') do |f|
43
+ # f.puts backup_script
44
+ # end
45
+ #
46
+ # backup_root_directory = File.expand_path('~/.cloud66/backup')
47
+ # FileUtils.mkdir_p(backup_root_directory)
48
+ #
49
+ # command = "backup perform --trigger #{script_name} --root-path #{backup_root_directory} --config_file #{random_file} 2>&1"
50
+ # @log.info "Shell command '#{command}'"
51
+ #
52
+ # begin
53
+ # result = run_shell("#{command}")
54
+ # data = result[:message]
55
+ #
56
+ # #regex to identify all the "error" lines
57
+ # errorRegex = Regexp.new('(?<line>\[\d{4}\/\d{2}\/\d{2}\s\d{2}\:\d{2}\:\d{2}\]\[\\e\[31merror\\e\[0m\].*?)(\\n|$)')
58
+ # errors = data.scan(errorRegex)
59
+ #
60
+ # if errors.size > 0
61
+ #
62
+ # #log all the errors
63
+ # @log.info errors
64
+ #
65
+ # #get rid of the backtrace
66
+ # removeRegex = Regexp.new('(Backtrace\:|\d{2}\:\d{2}\]\[\\e\[31merror\\e\[0m\]\s*\/)')
67
+ #
68
+ # #subregex to get rid of nasty timestamp and [\e[31merror\e[0m]
69
+ # replaceRegex = Regexp.new('^.*\[\\e\[31merror\\e\[0m\]')
70
+ #
71
+ # errorResult = []
72
+ # errors.each do |errorArr|
73
+ # error = errorArr[0]
74
+ # errorResult << error.gsub(replaceRegex,'').chomp unless error =~ removeRegex
75
+ # end
76
+ #
77
+ # run_result(false, errorResult.join("\n"))
78
+ # else
79
+ # if (result[:ok])
80
+ # run_result(true, "Backup completed successfully!")
81
+ # else
82
+ # run_result(true, "An error occurred!")
83
+ # end
84
+ # end
85
+ #
86
+ # rescue => ex
87
+ # run_result(false, "Failed to execute backup due to #{ex}")
88
+ # ensure
89
+ # File.delete random_file if File.exists? random_file
90
+ # end
91
+ # end
92
+ #end
@@ -0,0 +1,11 @@
1
+ require 'cloud66_agent/server'
2
+ require 'cloud66_agent/utils/vital_signs'
3
+ require 'cloud66_agent/utils/version'
4
+
5
+ module Cloud66
6
+ module Plugins
7
+ class Plugin
8
+ attr_accessor :version
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,92 @@
1
+ module Cloud66
2
+ module Utils
3
+ class Config
4
+
5
+ # default conf dir
6
+ CONFIG_PATH = "/etc/cloud66/cloud66_agent.yml"
7
+
8
+ attr_accessor :api_key,
9
+ :agent_uid,
10
+ :secret_key,
11
+ :log_path,
12
+ :log_level,
13
+ :url,
14
+ :faye_url,
15
+ :disabled
16
+
17
+ # load up the config at startup
18
+ def initialize
19
+ load if File.exists?(CONFIG_PATH)
20
+
21
+ # set defaults
22
+ @log_path = @log_path.nil? || @log_path.to_s.strip.empty? || @log_path == "STDOUT" ? STDOUT : @log_path
23
+ @log_level ||= 2
24
+ @url ||= 'https://api.cloud66.com'
25
+ @faye_url ||= 'https://sockets.cloud66.com/push'
26
+ @disabled ||= false
27
+ end
28
+
29
+ def is_agent_configured?
30
+ return !@agent_uid.nil? && !@agent_uid.empty?
31
+ end
32
+
33
+ def save
34
+ Dir.mkdir(CONFIG_PATH) if !FileTest::directory?(File.dirname(CONFIG_PATH))
35
+
36
+ File.open(CONFIG_PATH, 'w+') do |out|
37
+ data = {
38
+ 'api_key' => @api_key,
39
+ 'agent_uid' => @agent_uid,
40
+ 'secret_key' => @secret_key,
41
+ 'log_path' => @log_path == STDOUT ? "STDOUT" : @log_path,
42
+ 'log_level' => @log_level,
43
+ 'disabled' => @disabled
44
+ }
45
+ # store the url if it is different
46
+ data['url'] = @url if @url != 'https://api.cloud66.com'
47
+ # store the faye url if it is different
48
+ data['faye_url'] = @faye_url if @faye_url != 'https://sockets.cloud66.com/push'
49
+
50
+ YAML::dump(data, out)
51
+ end
52
+ end
53
+
54
+ def delete
55
+ File.delete(CONFIG_PATH) if File.exists?(CONFIG_PATH)
56
+ end
57
+
58
+ private
59
+
60
+ def load
61
+ raise "config not found" unless File.exists?(CONFIG_PATH)
62
+ config = YAML::load(File.open(CONFIG_PATH))
63
+
64
+ @api_key = config['api_key']
65
+ @agent_uid = config['agent_uid']
66
+ @secret_key = config['secret_key']
67
+
68
+ # get if it exists in the config
69
+ config_url = config['url']
70
+ @url = config_url if !config_url.nil? && !config_url.strip.empty?
71
+
72
+ # get if it exists in the config
73
+ config_faye_url = config['faye_url']
74
+ @faye_url = config_faye_url if !config_faye_url.nil? && !config_faye_url.strip.empty?
75
+
76
+ # get if it exists in the config
77
+ config_log_path = config['log_path']
78
+ @log_path = config_log_path if !config_log_path.nil? && !config_log_path.strip.empty?
79
+
80
+ # get if it exists in the config
81
+ config_log_level = config['log_level']
82
+ @log_level = config_log_level.to_i
83
+
84
+ disabled = config['disabled']
85
+ @disabled = disabled if !disabled.nil?
86
+ rescue
87
+ # we can't load the file
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,147 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module Cloud66
5
+ module Utils
6
+
7
+ class Server
8
+ include HTTParty
9
+
10
+ def self.send_configure(data)
11
+ process(do_post('/agent.json', build_content(data)))
12
+ end
13
+
14
+ def self.send_pulse
15
+ process(do_get("/agent/#{$config.agent_uid}/pulse.json", build_content))
16
+ end
17
+
18
+ def self.send_address(data)
19
+ process(do_post("/agent/#{$config.agent_uid}/address.json", build_content(data)))
20
+ end
21
+
22
+ def self.send_vitals(data)
23
+ process(do_post("/agent/#{$config.agent_uid}/vitals.json", build_content(data)))
24
+ end
25
+
26
+ private
27
+
28
+ def self.process(response)
29
+ $logger.debug "Received response!"
30
+ if response.code != 200
31
+ $logger.debug "Response code: #{response.code}"
32
+ $logger.debug "Response body: #{response.body}"
33
+ raise response.body
34
+ else
35
+ parsed_response = response.parsed_response
36
+ unless parsed_response['ok']
37
+ if parsed_response['shut_down']
38
+ $config.disabled = true
39
+ $config.save
40
+ exit -1
41
+ elsif parsed_response.has_key?('error')
42
+ raise parsed_response['error']
43
+ else
44
+ raise "unidentified error"
45
+ end
46
+ end
47
+ $logger.debug "Parsed response: #{parsed_response}"
48
+ parsed_response['data']
49
+ end
50
+ end
51
+
52
+ def self.do_post(url, content)
53
+ $logger.debug "Sending (post) request..."
54
+ $logger.debug "Request url: #{url}"
55
+ $logger.debug "Request content: #{content}"
56
+ base_uri $config.url
57
+ post(url, content)
58
+ end
59
+
60
+ def self.do_delete(url, content)
61
+ $logger.debug "Sending (delete) request..."
62
+ $logger.debug "Request url: #{url}"
63
+ $logger.debug "Request content: #{content}"
64
+ base_uri $config.url
65
+ delete(url, content)
66
+ end
67
+
68
+ def self.do_get(url, content)
69
+ $logger.debug "Sending (get) request..."
70
+ $logger.debug "Request url: #{url}"
71
+ $logger.debug "Request content: #{content}"
72
+ base_uri $config.url
73
+ get(url, content)
74
+ end
75
+
76
+ def self.do_put(url, content)
77
+ $logger.debug "Sending (put) request..."
78
+ $logger.debug "Request url: #{url}"
79
+ $logger.debug "Request content: #{content}"
80
+ base_uri $config.url
81
+ put(url, content)
82
+ end
83
+
84
+ def self.build_content(data = nil)
85
+ time = Time.now.utc.to_i
86
+ hash = Digest::SHA1.hexdigest("#{$config.api_key}#{$config.secret_key}#{time}").downcase
87
+ if data.nil?
88
+ content = { :headers => { 'api_key' => $config.api_key, 'hash' => hash, 'time' => time.to_s } }
89
+ else
90
+ content = { :headers => { 'api_key' => $config.api_key, 'hash' => hash, 'time' => time.to_s, 'Content-Type' => 'application/json' }, :body => { data: data }.to_json }
91
+ end
92
+ content
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ =begin
101
+ def get_job
102
+ process(self.class.get("/queue/#{@agent_id}.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key) } ))
103
+ end
104
+
105
+
106
+
107
+ def unregister(agent)
108
+ process(self.class.delete("/agent/#{@agent_id}.json", :headers => ClientAuth.build_headers(@api_key, @secret_key)))
109
+ end
110
+
111
+ def post_results(job_id, data)
112
+ process(self.class.post("/job/#{job_id}/complete.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
113
+ end
114
+
115
+ def pulse_without_ip_address
116
+ process(self.class.get("/agent/#{@agent_id}/pulse.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key) } ))
117
+ end
118
+
119
+ def pulse_with_ip_address(data)
120
+ process(self.class.post("/agent/#{@agent_id}/pulse.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
121
+ end
122
+
123
+ def status(stat)
124
+ data = { :status => stat }
125
+ process(self.class.post("/agent/#{@agent_id}/status.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json }))
126
+ end
127
+
128
+ def init(data)
129
+ process(self.class.post("/agent/#{@agent_id}/initialize.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json }))
130
+ end
131
+
132
+ def send_vital_signs(data)
133
+ process(self.class.post("/agent/#{@agent_id}/vitalsigns.json", { :headers => ClientAuth.build_headers(@api_key, @secret_key).merge({'Content-Type' => 'application/json'}), :body => data.to_json } ))
134
+ end
135
+
136
+ private
137
+
138
+ def process(response)
139
+ if response.code != 200
140
+ raise response.body
141
+ else
142
+ response.parsed_response
143
+ end
144
+ end
145
+ end
146
+ end
147
+ =end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+ module Cloud66
3
+ module Utils
4
+ class Version
5
+
6
+ ##
7
+ # Change the MAJOR, MINOR and PATCH constants below
8
+ # to adjust the version of the Cloud66 Agent gem
9
+ #
10
+ # MAJOR:
11
+ # Defines the major version
12
+ # MINOR:
13
+ # Defines the minor version
14
+ # PATCH:
15
+ # Defines the patch version
16
+ MAJOR, MINOR, PATCH = 0, 0, 1
17
+
18
+ #ie. PRERELEASE_MODIFIER = 'beta1'
19
+ PRERELEASE_MODIFIER = "pre2"
20
+
21
+ ##
22
+ # Returns the major version ( big release based off of multiple minor releases )
23
+ def self.major
24
+ MAJOR
25
+ end
26
+
27
+ ##
28
+ # Returns the minor version ( small release based off of multiple patches )
29
+ def self.minor
30
+ MINOR
31
+ end
32
+
33
+ ##
34
+ # Returns the patch version ( updates, features and (crucial) bug fixes )
35
+ def self.patch
36
+ PATCH
37
+ end
38
+
39
+ ##
40
+ # Returns the prerelease modifier ( not quite ready for public consumption )
41
+ def self.prerelease_modifier
42
+ PRERELEASE_MODIFIER
43
+ end
44
+
45
+ ##
46
+ # Returns the current version of the Backup gem ( qualified for the gemspec )
47
+ def self.current
48
+ prerelease_modifier.nil? ? "#{major}.#{minor}.#{patch}" : "#{major}.#{minor}.#{patch}.#{prerelease_modifier}"
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,172 @@
1
+ require 'sys/filesystem'
2
+ require 'socket'
3
+
4
+ module Cloud66
5
+ module Utils
6
+ class VitalSigns
7
+
8
+ def self.system_info
9
+ return { ext_ipv4: "123.123.123.123",
10
+ int_ipv4: "123.123.123.123",
11
+ ext_ipv6: "123.123.123.123",
12
+ int_ipv6: "123.123.123.123" } if RUBY_PLATFORM.include?('darwin')
13
+
14
+ # system info
15
+ return parse_data(`sudo facter`)
16
+ end
17
+
18
+ def self.address_info
19
+ return { ext_ipv4: "123.123.123.123",
20
+ int_ipv4: "123.123.123.123",
21
+ ext_ipv6: "123.123.123.123",
22
+ int_ipv6: "123.123.123.123" } if RUBY_PLATFORM.include?('darwin')
23
+
24
+ # address information
25
+ hash = parse_data(`facter ipaddress ec2_public_ipv4 ipaddress_eth0 ipaddress6 ipaddress6_eth0`)
26
+
27
+ result = {}
28
+
29
+ if hash.has_key?('ec2_public_ipv4')
30
+ # return ec2 info first (most specific)
31
+ result[:ext_ipv4] = hash['ec2_public_ipv4']
32
+ elsif hash.has_key?('ipaddress')
33
+ # return ip address next (general)
34
+ result[:ext_ipv4] = hash['ipaddress']
35
+ end
36
+ result[:int_ipv4] = hash['ipaddress_eth0'] if hash.has_key?('ipaddress_eth0')
37
+ result[:ext_ipv6] = hash['ipaddress6'] if hash.has_key?('ipaddress6')
38
+ result[:int_ipv6] = hash['ipaddress6_eth0'] if hash.has_key?('ipaddress6_eth0')
39
+
40
+ # don't have any ip address info
41
+ raise "no address information" if result.empty? || !result.has_key?(:ext_ipv4)
42
+
43
+ # return ip address info
44
+ return result
45
+ end
46
+
47
+ def self.vitals_info
48
+ return {
49
+ disk: Utils::VitalSigns.disk_usage_info,
50
+ cpu: Utils::VitalSigns.cpu_usage_info,
51
+ memory: Utils::VitalSigns.memory_usage_info
52
+ }
53
+ end
54
+
55
+ private
56
+
57
+ def self.disk_usage_info
58
+ space_info = {}
59
+ Sys::Filesystem.mounts do |mount|
60
+ stat = Sys::Filesystem.stat(mount.mount_point)
61
+
62
+ #skip if this mount is not active
63
+ #next if stat.blocks_available == 0 && stat.blocks == 0
64
+
65
+ mb_free = Float(stat.block_size) * Float(stat.blocks_available) / 1000 / 1000
66
+ mb_total = Float(stat.block_size) * Float(stat.blocks) / 1000 / 1000
67
+ mb_used = mb_total - mb_free
68
+ percent_used = mb_total > 0.0 ? mb_used / mb_total * 100 : 0.0
69
+
70
+ space_info[mount.mount_point] = { mb_free: mb_free, mb_used: mb_used, mb_total: mb_total, percent_used: percent_used }
71
+ mount.mount_point
72
+ end
73
+ return space_info
74
+ end
75
+
76
+ def self.cpu_usage_info
77
+
78
+ #NOTE: we can get core-level info with mpstat -P ALL 1 1
79
+ #parse mpstat result
80
+ mpstat_result = `mpstat 1 5`
81
+
82
+ # mpstat_result = <<-SAMPLE
83
+ #Linux 3.2.0-23-generic (precise64) 12/07/2012 _x86_64_ (2 CPU)
84
+ #
85
+ #10:42:50 AM CPU %usr %nice %sys %ddle %irq %soft %steal %guest %idle
86
+ #10:42:51 AM all 0.00 0.00 0.50 5.00 0.00 0.00 0.00 0.00 99.50
87
+ #Average: all 0.00 0.00 0.50 50.00 0.00 0.00 0.00 0.00 99.50
88
+ #SAMPLE
89
+
90
+ #split output into lines
91
+ lines = mpstat_result.split(/\r?\n/)
92
+
93
+ #get rid of time (first 13 chars)
94
+ lines = lines.map { |line| line[13..-1] }
95
+
96
+ #get the header line and split into columns
97
+ header_line = lines.detect { |line| line =~ /%idle/ }
98
+ columns = header_line.split(/\s+/)
99
+
100
+ #detect position of %idle column
101
+ idle_index = columns.index('%idle')
102
+
103
+ #get average line
104
+ average_line = lines[-1]
105
+ columns = average_line.split(/\s+/)
106
+
107
+ #get idle value
108
+ idle_string = columns[idle_index]
109
+ idle_value = idle_string.to_f
110
+
111
+ percent_used = 100.0 - idle_value
112
+
113
+ #get average utilization value
114
+ return { percent_used: percent_used, percent_free: idle_value }
115
+ end
116
+
117
+ def self.memory_usage_info
118
+ free_m_result = `free -m`
119
+ # free_m_result = <<-SAMPLE
120
+ # total used free shared buffers cached
121
+ #Mem: 590 480 109 0 37 227
122
+ #-/+ buffers/cache: 216 373
123
+ #Swap: 0 0 0
124
+ #SAMPLE
125
+
126
+ free_m_result.each_line do |line|
127
+ if line =~ /^Mem:/
128
+ parts = line.scan(/.*?(\d+)/)
129
+ parts.flatten!
130
+
131
+ mb_total = parts[0].to_f
132
+ # mb_used is not a true representation due to OS gobbling up mem
133
+ # mb_used = parts[1].to_i
134
+ mb_free = parts[2].to_f
135
+ mb_shared = parts[3].to_f
136
+ mb_buffers = parts[4].to_f
137
+ mb_cached = parts[5].to_f
138
+
139
+ #The total free memory available to proceses is calculated by adding up Mem:cached + Mem:buffers + Mem:free (99 + 63 + 296)
140
+ #This then needs to be divided by Mem:total to get the total available free memory (1692)
141
+ mb_available = mb_cached + mb_buffers + mb_free
142
+ mb_used = mb_total - mb_available
143
+ mb_free = mb_total - mb_used
144
+ percent_used = mb_used / mb_total * 100
145
+ return { mb_free: mb_free, mb_used: mb_used, mb_total: mb_total, percent_used: percent_used }
146
+ end
147
+ end
148
+ end
149
+
150
+ # parse the data, not using YAML due to YAML parsing issues and JSON output not always working
151
+ def self.parse_data(data)
152
+ hash = {}
153
+ data.lines.each do |line|
154
+ split = line.split('=>')
155
+ key = split[0]
156
+ if split.size == 2
157
+ value = split[1]
158
+ unless value.nil?
159
+ value = value.strip
160
+ # exclude empty or long results (like ssh keys)
161
+ if !value.empty? && value.size < 100
162
+ key = key.strip
163
+ hash[key] = value
164
+ end
165
+ end
166
+ end
167
+ end
168
+ return hash
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,20 @@
1
+ # require all commands
2
+ Dir.glob(File.dirname(File.absolute_path(__FILE__)) + '/cloud66_agent/commands/*', &method(:require))
3
+
4
+ class Cloud66Agent
5
+ def self.configure(server_uid)
6
+ Cloud66::Commands::Configure.perform server_uid
7
+ end
8
+
9
+ def self.pulse
10
+ Cloud66::Commands::Pulse.perform
11
+ end
12
+
13
+ def self.vitals
14
+ Cloud66::Commands::Vitals.perform
15
+ end
16
+
17
+ def self.address
18
+ Cloud66::Commands::Address.perform
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloud66_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre2
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Cloud 66
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: sys-filesystem
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ description: See https://cloud66.com for more info
47
+ email:
48
+ - hello@cloud66.com
49
+ executables:
50
+ - cloud66_agent
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - bin/cloud66_agent
60
+ - cloud66_agent.gemspec
61
+ - lib/cloud66_agent.rb
62
+ - lib/cloud66_agent/commands/address.rb
63
+ - lib/cloud66_agent/commands/configure.rb
64
+ - lib/cloud66_agent/commands/pulse.rb
65
+ - lib/cloud66_agent/commands/vitals.rb
66
+ - lib/cloud66_agent/plugins/backup.rb
67
+ - lib/cloud66_agent/plugins/plugin.rb
68
+ - lib/cloud66_agent/utils/config.rb
69
+ - lib/cloud66_agent/utils/server.rb
70
+ - lib/cloud66_agent/utils/version.rb
71
+ - lib/cloud66_agent/utils/vital_signs.rb
72
+ homepage: http://cloud66.com
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>'
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.1
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.25
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Cloud 66 server component
96
+ test_files: []