cloudstats 0.5.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 901b4e7eb8fcbe02e904e270563230c003d32896
4
+ data.tar.gz: 3f1d30106500d8439151b1331fce2df0c89b425b
5
+ SHA512:
6
+ metadata.gz: 79449f16ba287d92ba8f6924c386e86a9e415eab28f082e11a4b932d045115807bf42673f133398f9780dc8f43c1b2fb50671389876465fca719c440ed9a193f
7
+ data.tar.gz: 2b38b87f2b14de75a76d7ea27c385a01c0077c3184c98d56a1ff755677e14714891d709195d848b09e9322a7323248187e43d2daac93b10d5a84bf45c3ff437e
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.13.7
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloudstats.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 niwo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ # Cloudstats
2
+
3
+ Cloudstats pulls project and account statistics from the CloudStack API and feeds them into a Influxdb database.
4
+ Whit the help of Grafana this let's you craft beautiful usage dashboards.
5
+
6
+ ## Installation
7
+
8
+ Install the gem as:
9
+
10
+ $ gem install cloudstats
11
+
12
+ ## Usage
13
+
14
+ Make sure you have a working [cloudstack_client](https://github.com/niwo/cloudstack_client) configuration file in your home directory.
15
+ Usually this is found under `~/.cloudstack.yml`
16
+
17
+ Install gem dependencies:
18
+
19
+ $ bundle install
20
+
21
+ See the help for more options:
22
+
23
+ $ bin/cloudstats help
24
+
25
+
26
+ ## Development
27
+
28
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
29
+
30
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
31
+
32
+ ## Contributing
33
+
34
+ Bug reports and pull requests are welcome on GitHub at https://github.com/niwo/cloudstats.
35
+
36
+ ## License
37
+
38
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # resolve bin path, ignoring symlinks
5
+ # resolve path, ignoring symlinks
6
+ require "pathname"
7
+ $:.unshift File.expand_path(
8
+ "../../lib", Pathname.new(__FILE__).realpath
9
+ )
10
+ require "cloudstats/cli"
11
+
12
+ Cloudstats::Cli.start
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cloudstats"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloudstats/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cloudstats"
8
+ spec.version = Cloudstats::VERSION
9
+ spec.authors = ["niwo"]
10
+ spec.email = ["nik.wolfgramm@gmail.com"]
11
+
12
+ spec.summary = %q{Collect CloudStack stats and feed them to influxdb.}
13
+ spec.description = %q{Collect project and account statistics from the CloudStack API and feeds them into a influxdb.}
14
+ spec.homepage = "https://github.com/niwo/cloudstats"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.executables = %w(cloudstats)
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 12.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+
27
+ spec.add_runtime_dependency "thor", "~> 0.19"
28
+ spec.add_runtime_dependency "cloudstack_client", "~> 1.5.3"
29
+ end
@@ -0,0 +1 @@
1
+ require "cloudstats/version"
@@ -0,0 +1,117 @@
1
+ require "thor"
2
+ require "cloudstats/version"
3
+ require "cloudstats/collect"
4
+ require "cloudstats/feed"
5
+
6
+ module Cloudstats
7
+ class Cli < Thor
8
+ include Thor::Actions
9
+
10
+ package_name "cloudstats"
11
+
12
+ class_option :cloudstack_url,
13
+ default: "http://localhost:8080/client/api",
14
+ aliases: '-A',
15
+ desc: 'CloudStack API URL'
16
+
17
+ class_option :cloudstack_api_key,
18
+ aliases: '-k',
19
+ desc: 'CloudStack API Key'
20
+
21
+ class_option :cloudstack_secret_key,
22
+ aliases: '-s',
23
+ desc: 'CloudStack API Secret'
24
+
25
+ class_option :cloudstack_config,
26
+ default: CloudstackClient::Configuration.locate_config_file,
27
+ aliases: '-C',
28
+ desc: 'Location of your cloudstack-cli configuration file'
29
+
30
+ class_option :env,
31
+ aliases: '-E',
32
+ desc: 'cloudstack-cli environment to use'
33
+
34
+ class_option :influx_url,
35
+ default: "http://localhost:8086/",
36
+ aliases: '-U',
37
+ desc: 'Influxdb URL'
38
+
39
+ class_option :influx_user,
40
+ default: "cloudstack-stats",
41
+ aliases: '-u',
42
+ desc: 'Influxdb user'
43
+
44
+ class_option :influx_password,
45
+ aliases: '-p',
46
+ desc: 'Influxdb password'
47
+
48
+ class_option :database,
49
+ default: "cloudstack-stats",
50
+ aliases: '-D',
51
+ desc: 'Influxdb database'
52
+
53
+ class_option :debug,
54
+ aliases: '-D',
55
+ desc: 'Enable debug output',
56
+ type: :boolean,
57
+ default: false
58
+
59
+ # catch control-c and exit
60
+ trap("SIGINT") do
61
+ puts
62
+ puts "exiting.."
63
+ exit!
64
+ end
65
+
66
+ # exit with return code 1 in case of a error
67
+ def self.exit_on_failure?
68
+ true
69
+ end
70
+
71
+ desc "version", "Print version number"
72
+ def version
73
+ say "cloudstats v#{Cloudstats::VERSION}"
74
+ end
75
+ map %w(-v --version) => :version
76
+
77
+ desc "projects", "Pull projects stats from CloudStack."
78
+ option :domain,
79
+ desc: "Name of Domain (for recursive search)",
80
+ default: "ROOT"
81
+ def projects
82
+ say "Collect stats...", :yellow
83
+ stats = Collect.new(options).project_stats
84
+ say "Write stats to influxdb...", :yellow
85
+ Feed.new(options).write(stats) {|stat, res| print_status(stat, res)}
86
+ rescue => e
87
+ say "ERROR: ", :red
88
+ puts e.message
89
+ end
90
+
91
+ desc "accounts", "Pull account stats from CloudStack."
92
+ option :domain,
93
+ desc: "Name of Domain (for recursive search)",
94
+ default: "ROOT"
95
+ def accounts
96
+ say "Collect stats...", :yellow
97
+ stats = Collect.new(options).account_stats
98
+ say "Write stats to influxdb...", :yellow
99
+ Feed.new(options).write(stats) {|stat, res| print_status(stat, res)}
100
+ # rescue => e
101
+ # say "ERROR: ", :red
102
+ # puts e.message
103
+ end
104
+
105
+ no_commands do
106
+ def print_status(stat, res)
107
+ print_in_columns [
108
+ stat['name'],
109
+ res.code == '204' ?
110
+ "OK (HTTP #{res.code})" :
111
+ "FAIL (HTTP #{res.code})"
112
+ ]
113
+ end
114
+ end
115
+
116
+ end # class
117
+ end # module
@@ -0,0 +1,65 @@
1
+ require "cloudstack_client"
2
+ require "cloudstack_client/configuration"
3
+ require "yaml"
4
+
5
+ module Cloudstats
6
+ class Collect
7
+
8
+ def initialize(settings)
9
+ @settings = settings.dup
10
+ @config = if @settings[:cloudstack_url] &&
11
+ @settings[:cloudstack_api_key] &&
12
+ @settings[:cloudstack_secret_key]
13
+ {
14
+ url: @settings[:cloudstack_url],
15
+ api_key: @settings[:cloudstack_api_key],
16
+ secret_key: @settings[:cloudstack_secret_key]
17
+ }
18
+ else
19
+ @settings[:config_file] = @settings[:cloudstack_config]
20
+ CloudstackClient::Configuration.load(@settings)
21
+ end
22
+ @cs ||= CloudstackClient::Client.new(
23
+ @config[:url],
24
+ @config[:api_key],
25
+ @config[:secret_key]
26
+ )
27
+ @cs.debug = true if @settings[:debug]
28
+ @cs
29
+ end
30
+
31
+ def account_stats
32
+ {
33
+ type: "account",
34
+ stats: @cs.list_accounts(client_options)
35
+ }
36
+ end
37
+
38
+ def project_stats
39
+ {
40
+ type: "project",
41
+ stats: @cs.list_projects(client_options)
42
+ }
43
+ end
44
+
45
+ private
46
+
47
+ def client_options
48
+ { listall: true, isrecursive: true }.merge(
49
+ resolve_domain(@settings)
50
+ )
51
+ end
52
+
53
+ def resolve_domain(opts)
54
+ if opts[:domain]
55
+ if domain = @cs.list_domains(name: opts[:domain]).first
56
+ opts[:domainid] = domain['id']
57
+ else
58
+ raise "Domain #{opts[:domain]} not found."
59
+ end
60
+ end
61
+ opts
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,88 @@
1
+ module Cloudstats
2
+ CS_TAGS = %w(domain)
3
+
4
+ CS_STATS = %w(
5
+ vmrunning vmstopped
6
+ memorytotal cputotal iptotal
7
+ primarystoragetotal
8
+ secondarystoragetotal
9
+ snapshottotal networktotal
10
+ volumetotal sentbytes
11
+ )
12
+
13
+ class Feed
14
+ require "net/http"
15
+ require "uri"
16
+
17
+ def initialize(opts = {})
18
+ @database = opts[:database]
19
+ @url = opts[:influx_url] ||
20
+ "http://localhost:8086/"
21
+ @user = opts[:influx_user]
22
+ @password = opts[:influx_password]
23
+ @debug = opts[:debug]
24
+ @total = {type: "total", stats: [{}]}
25
+ end
26
+
27
+ def write(stats, total = true)
28
+ uri = URI.parse(
29
+ URI.join(
30
+ @url,
31
+ "write?db=#{@database}&precision=m"
32
+ ).to_s
33
+ )
34
+ http = Net::HTTP.new(uri.host, uri.port)
35
+
36
+ if uri.scheme == "https"
37
+ http.use_ssl = true
38
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
39
+ end
40
+
41
+ request = Net::HTTP::Post.new(uri.request_uri)
42
+ request.content_type = "application/octet-stream"
43
+ request.basic_auth(@user, @password) if @user && @password
44
+
45
+
46
+ type = stats[:type]
47
+ stats[:stats].each do |stat|
48
+ add_to_total(stat) if total
49
+ request.body = line = stat_to_line(stat, type)
50
+ puts line if @debug
51
+ response = http.request(request)
52
+ yield(stat, response) if block_given?
53
+ end
54
+ if total
55
+ @total[:stats][0]['name'] = type
56
+ write(@total, total = false) {|stat, response| yield(stat, response)}
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # builds influxdb line protocol strings
63
+ def stat_to_line(obj, type)
64
+ fields = Cloudstats::CS_STATS.map {|name| "#{name}=#{obj[name].to_i}i" }
65
+ unless type == "total"
66
+ tags = Cloudstats::CS_TAGS.map {|name| "#{name}=#{obj[name]}" }
67
+ end
68
+ line = "#{type}.#{normalize_name(obj["name"])},type=#{type}"
69
+ line += "," + tags.join(",") if tags
70
+ line += " " + fields.join(",")
71
+ end
72
+
73
+ def add_to_total(obj)
74
+ Cloudstats::CS_STATS.each do |stat|
75
+ if @total[:stats][0].key? stat
76
+ @total[:stats][0][stat] += obj[stat].to_i
77
+ else
78
+ @total[:stats][0][stat] = obj[stat].to_i
79
+ end
80
+ end
81
+ end
82
+
83
+ def normalize_name(name)
84
+ name.downcase.tr(" ", "-").tr("--", "-").gsub(/[^0-9A-Za-z\-\_]/, '')
85
+ end
86
+
87
+ end # class
88
+ end # module
@@ -0,0 +1,3 @@
1
+ module Cloudstats
2
+ VERSION = "0.5.0"
3
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudstats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - niwo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.19'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.19'
69
+ - !ruby/object:Gem::Dependency
70
+ name: cloudstack_client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.5.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.5.3
83
+ description: Collect project and account statistics from the CloudStack API and feeds
84
+ them into a influxdb.
85
+ email:
86
+ - nik.wolfgramm@gmail.com
87
+ executables:
88
+ - cloudstats
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/cloudstats
99
+ - bin/console
100
+ - bin/setup
101
+ - cloudstats.gemspec
102
+ - lib/cloudstats.rb
103
+ - lib/cloudstats/cli.rb
104
+ - lib/cloudstats/collect.rb
105
+ - lib/cloudstats/feed.rb
106
+ - lib/cloudstats/version.rb
107
+ homepage: https://github.com/niwo/cloudstats
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.5.2
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Collect CloudStack stats and feed them to influxdb.
131
+ test_files: []