cloud66_agent 0.0.1.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []