oneacct-export 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 670e8800aaf7a64b5f75d0f6d930e0d9be4d96fb
4
- data.tar.gz: e6545c409b1c10e466a927833d3ffcde5b0c9c19
3
+ metadata.gz: e251289fc61da90a2cd802b46c6a9bb432a267b4
4
+ data.tar.gz: 1ee83f262bdf1234ce6e9c90665a8b4716293651
5
5
  SHA512:
6
- metadata.gz: 79f668da9123a2b2d83eb3131062494614ce605132b394921b0867faeb72c53c7a16649095e31928f6e59eb0163b4e78574670515cd928dfbaf75914bdf708bc
7
- data.tar.gz: fe9e971c3b06d3f7717e24feca0379206f4e8123276d01815618c475b331fbcf2e30db1bad2cbfa19b0190615ff3d759950d2f097c25bd9e913596f15c822fa2
6
+ metadata.gz: 4fb58b1c9b0c824a1a1dbc3aecc6e43fcc96de03dc915235a09d6b36d7a44ddf6a7dbaf92724afbfadfeec47229bb78c060233389f5dc298450f098b59d0637f
7
+ data.tar.gz: b505e55867d9defd5682e9ea50a2a12ee8f5609ade511122d21bccca7b0abf14e07c92b7a59c2bb19982cd6c627dfdb6ccd4640f6cab65ddb2800001b2582715
data/.travis.yml ADDED
@@ -0,0 +1,36 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1
6
+ - ruby-head
7
+ - jruby-19mode
8
+ - jruby-head
9
+
10
+ jdk:
11
+ - openjdk7
12
+ - oraclejdk7
13
+ - openjdk6
14
+
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: ruby-head
18
+ - rvm: jruby-head
19
+ exclude:
20
+ - rvm: 2.0.0
21
+ jdk: openjdk7
22
+ - rvm: 2.0.0
23
+ jdk: oraclejdk7
24
+ - rvm: 2.1
25
+ jdk: openjdk7
26
+ - rvm: 2.1
27
+ jdk: oraclejdk7
28
+ - rvm: ruby-head
29
+ jdk: openjdk7
30
+ - rvm: ruby-head
31
+ jdk: oraclejdk7
32
+ fast_finish: true
33
+
34
+ branches:
35
+ only:
36
+ - master
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --title "Documentation of the OneacctExport gem" --markup markdown --private
data/README.md CHANGED
@@ -1,9 +1,126 @@
1
1
  # OneacctExport
2
-
3
2
  Exporting OpenNebula accounting data.
4
3
 
5
- ## Contributing
4
+ [![Build Status](https://secure.travis-ci.org/Misenko/oneacct_export.png)](http://travis-ci.org/Misenko/oneacct_export)
5
+ [![Dependency Status](https://gemnasium.com/Misenko/oneacct_export.png)](https://gemnasium.com/Misenko/oneacct_export)
6
+ [![Gem Version](https://fury-badge.herokuapp.com/rb/oneacct-export.png)](https://badge.fury.io/rb/oneacct-export)
7
+ [![Code Climate](https://codeclimate.com/github/Misenko/oneacct_export.png)](https://codeclimate.com/github/Misenko/oneacct_export)
8
+
9
+
10
+ ##Requirements
11
+ * Ruby >= 2.0
12
+ * Rubygems
13
+ * Redis server (doesn't have to be present on the same machine)
14
+ * OpenNebula >= 4.4 (doesn't have to be present on the same machine)
15
+
16
+ ##Installation
17
+ ###From distribution specific packages
18
+ Distribution specific packages can be created with [omnibus packaging for OneacctExport](https://github.com/Misenko/omnibus-oneacct-export). When installing via packages you don't have to install neither ruby nor
19
+ rubygems. Packages contain embedded ruby and all the necessary gems and libraries witch will not effect your system ruby, gems and libraries.
20
+
21
+ Currently supported distributions:
22
+
23
+ * Ubuntu 10.04
24
+ * Ubuntu 12.04
25
+ * Ubuntu 14.04
26
+ * Debian 6.0.10
27
+ * Debian 7.6
28
+ * CentOS 5.10
29
+ * CentOS 6.5
30
+
31
+ ###From RubyGems.org
32
+ To install the most recent stable version
33
+ ```bash
34
+ gem install oneacct-export
35
+ ```
36
+
37
+ ###From source (dev)
38
+ **Installation from source should never be your first choice! Especially, if you are not familiar with RVM, Bundler, Rake and other dev tools for Ruby!**
39
+
40
+ **However, if you wish to contribute to our project, this is the right way to start.**
41
+
42
+ To build and install the bleeding edge version from master
43
+
44
+ ```bash
45
+ git clone git://github.com/Misenko/oneacct_export.git
46
+ cd oneacct_export
47
+ gem install bundler
48
+ bundle install
49
+ bundle exec rake spec
50
+ rake install
51
+ ```
52
+ ##Configuration
53
+ ###Create a configuration file for OneacctExport
54
+ Configuration file can be read by OneacctExport from these three locations:
55
+
56
+ * ~/.oneacct-export/conf.yml
57
+ * /etc/oneacct-export/conf.yml
58
+ * <PATH_TO_GEM_DIR>/config/conf.yml
59
+
60
+ The example configuration file can be found at the last location <PATH_TO_GEM_DIR>/config/conf.yml. When editing a configuration file you have to follow the division into three environments: production,
61
+ development and test. All the configuration options are described in the example configuration file.
62
+
63
+ ###Create a configuration file for Sidekiq
64
+ Sidekiq configuration file can be placed anywhere you want since you will provide path to the configuration later during the Sidekiq start. How the Sidekiq configuration should look like and what options you can use
65
+ can be found on its [wiki page](https://github.com/mperham/sidekiq/wiki/Advanced-Options).
66
+
67
+ The important thing is to set the same queue name in both OneacctExport and Sidekiq configuration files. OneacctExport is currently supporting adding jobs to only one queue.
68
+
69
+ ###Configure RPC connection
70
+ RPC connection for OpenNebula can be configured in two ways:
6
71
 
72
+ * Via OneacctExport configuration file, option xml_rpc and its suboptions
73
+ * Via Opennebula configuration mechanism:
74
+
75
+ System environment variable ONE_AUTH contains path to the file containing string in format username:password to authenticate against OpenNebula. If the variable is empty, default file location is ~/.one/one_auth.
76
+
77
+ System environment variable ONE_XMLRPC contains URL of OpenNebula RPC gate. If empty, the same information can be stored in ~/.one/one_endpoint
78
+
79
+ ###Set Rails environment variable according to your environment
80
+ You have to set system environment variable RAILS_ENV to one of the values production, development or test. OneacctExport is not a Rails application but we chose the Rails variable for easier possible integration in
81
+ the future.
82
+
83
+ ##Usage
84
+
85
+ **Both Opennebula and Redis server must be running prior the next steps.**
86
+
87
+ ###Start sidekiq
88
+ First you have to start Sidekiq so it can run the jobs from the queue. Since OneacctExport is not a Rails application Sidekiq has to be started with OneacctExport's worker class as an argument. For example:
89
+
90
+ ```bash
91
+ sidekiq -r <PATH_TO_GEM_DIR>/lib/one_worker.rb -C <PATH_TO_SIDEKIQ_CONF>/sidekiq.yml
92
+ ```
93
+
94
+ ###Start OneacctExport
95
+
96
+ OneacctExport is run with executable `oneacct-export`. For a list of all available options run `oneacct-export -h`:
97
+
98
+ ```
99
+ $ oneacct-export -h
100
+
101
+ Usage oneacct-export [options]
102
+
103
+ --records-from TIME Retrieves only records newer than TIME
104
+ --records-to TIME Retrieves only records older than TIME
105
+ --include-groups GROUP1[,GROUP2,...]
106
+ Retrieves only records of virtual machines which belong to the specified groups
107
+ --exclude-groups GROUP1[,GROUP2,...]
108
+ Retrieves only records of virtual machines which don't belong to the specified groups
109
+ --group-file FILE If --include-groups or --exclude-groups specified, loads groups from file FILE
110
+ -b, --[no-]blocking Run in a blocking mode - wait until all submitted jobs are processed
111
+ -t, --timeout N Timeout for blocking mode in seconds. Default is 1 hour.
112
+ -c, --[no-]compatibility-mode Run in compatibility mode - supports OpenNebula 4.4.x
113
+ -h, --help Shows this message
114
+ -v, --version Shows version
115
+ ```
116
+
117
+ ##Code Documentation
118
+ [Code Documentation for OneacctExport by YARD](http://rubydoc.info/github/Misenko/oneacct_export/)
119
+
120
+ ##Continuous integration
121
+ [Continuous integration for OneacctExport by Travis-CI](http://travis-ci.org/Misenko/oneacct_export/)
122
+
123
+ ## Contributing
7
124
  1. Fork it ( https://github.com/Misenko/oneacct_export/fork )
8
125
  2. Create your feature branch (`git checkout -b my-new-feature`)
9
126
  3. Commit your changes (`git commit -am 'Add some feature'`)
data/bin/oneacct-export CHANGED
@@ -8,16 +8,19 @@ require 'settings'
8
8
  require 'fileutils'
9
9
  require 'oneacct_opts'
10
10
 
11
+ #parse options from command line
11
12
  options = OneacctOpts.parse(ARGV)
12
13
 
14
+ #initialize default logger
13
15
  log = Logger.new(STDOUT)
14
16
 
17
+ #initialize specific logger according to the configuration
15
18
  if Settings['logging'] && Settings['logging']['log_type'] == 'file'
16
19
  begin
17
20
  log_file = File.open(Settings['logging']['log_file'], File::WRONLY | File::CREAT | File::APPEND)
18
21
  log = Logger.new(log_file)
19
22
  rescue => e
20
- OneacctExporter::Log.setup_logging(log)
23
+ OneacctExporter::Log.setup_log_level(log)
21
24
  log.warn("Unable to create log file #{Settings['logging']['log_file']}: #{e.message}.\
22
25
  Falling back to STDOUT.")
23
26
  end
@@ -35,6 +38,7 @@ groups = {}
35
38
  groups[:include] = options.include_groups if options.include_groups
36
39
  groups[:exclude] = options.exclude_groups if options.exclude_groups
37
40
 
41
+ #read groups restriction from file if chosen
38
42
  if options.groups_file
39
43
  log.debug('Reading groups from file...')
40
44
  if File.exist?(options.groups_file) && File.readable?(options.groups_file)
@@ -50,6 +54,7 @@ if options.groups_file
50
54
  end
51
55
  end
52
56
 
57
+ #create output directory
53
58
  begin
54
59
  FileUtils.mkdir_p Settings.output['output_dir']
55
60
  rescue SystemCallError => e
@@ -68,5 +73,6 @@ opts[:compatibility] = options.compatibility
68
73
 
69
74
  log.debug(opts)
70
75
 
76
+ #run the export
71
77
  oneacct_exporter = OneacctExporter.new(opts, log)
72
78
  oneacct_exporter.export
data/config/conf.yml CHANGED
@@ -1,30 +1,31 @@
1
- #Five mandatory parameters:
1
+ #There are six mandatory parameters:
2
2
  # site_name
3
3
  # cloud_type
4
4
  # endpoint
5
5
  # output -> output_dir
6
6
  # output -> output_type
7
+ # sidekiq -> queue
7
8
  defaults: &defaults
8
- site_name: <fill in>
9
- cloud_type: <fill in>
10
- endpoint: <fill in>
9
+ site_name: <fill in> #Usually a shor provider name, e.g. CESNET
10
+ cloud_type: OpenNebula
11
+ endpoint: <fill in> #URL of your OCCI endpoint, e.g. https://fqdn.example.com:80
11
12
  output:
12
- output_dir: <fill in>
13
- output_type: apel-0.2 #currently only option
14
- # num_of_vms_per_file: 500
13
+ output_dir: <fill in> #Directory for outgoing messages
14
+ output_type: apel-0.2 #Format of outgoing messages. apel-0.2 is currently the only option.
15
+ # num_of_vms_per_file: 500 #Maximum number of virtual machine records per one output file.
15
16
  #
16
- #logging:
17
- # log_type: file #Two options: file, syslog. Defaults to stdout
18
- # log_file: /var/log/oneacct-export.log #Used when type file selected
17
+ # logging:
18
+ # log_type: file #Two options: file, syslog. Defaults to stdout
19
+ # log_file: /var/log/oneacct-export.log #Used when type file selected
19
20
  #
20
- #xml_rpc:
21
- # secret: username:password #If not specified looking for secret in ONE_AUTH and ~/.one/one_auth
22
- # endpoint: http://localhost:2633/RPC2 #Defaults to content of ONE_XMLRPC or content of ~/.one/one_endpoint or http://localhost:2633/RPC2
21
+ # xml_rpc:
22
+ # secret: username:password #If not specified looking for secret in ONE_AUTH and ~/.one/one_auth.
23
+ # endpoint: http://localhost:2633/RPC2 #Defaults to content of ONE_XMLRPC or content of ~/.one/one_endpoint or http://localhost:2633/RPC2.
23
24
  #
24
- #redis:
25
- # namespace: oneacct_export
26
- # url: redis://localhost:6379
27
- # password: password
25
+ # redis:
26
+ # namespace: oneacct_export
27
+ # url: redis://localhost:6379 #URL of redis server, defaults to redis://localhost:6379.
28
+ # password: password #Password to access redis server if needed.
28
29
  sidekiq:
29
30
  queue: oneacct_export
30
31
  production:
@@ -1,5 +1,6 @@
1
1
  require 'uri'
2
2
 
3
+ # Simple helper module for input validation
3
4
  module InputValidator
4
5
  URI_RE = /\A#{URI.regexp}\z/
5
6
  NUMBER_RE = /\A[[:digit:]]+\z/
@@ -4,6 +4,12 @@ require 'errors'
4
4
  require 'logger'
5
5
  require 'input_validator'
6
6
 
7
+ # Class for accessing OpenNebula via XML RPC and requesting accounting data
8
+ #
9
+ # @attr_reader [any logger] logger
10
+ # @attr_reader [Integer] batch_size number of vm records to request
11
+ # @attr_reader [OpenNebula::Client] client client for communicaton with OpenNebula
12
+ # @attr_reader [TrueClass, FalseClass] compatibility whether or not communicate in compatibility mode (omit some newer API functions)
7
13
  class OneDataAccessor
8
14
  include Errors
9
15
  include InputValidator
@@ -24,6 +30,7 @@ class OneDataAccessor
24
30
  initialize_client
25
31
  end
26
32
 
33
+ # Initialize OpenNebula client for further connection
27
34
  def initialize_client
28
35
  secret = Settings['xml_rpc'] ? Settings.xml_rpc['secret'] : nil
29
36
  endpoint = Settings['xml_rpc'] ? Settings.xml_rpc['endpoint'] : nil
@@ -32,9 +39,16 @@ class OneDataAccessor
32
39
  @client = OpenNebula::Client.new(secret, endpoint)
33
40
  end
34
41
 
42
+ # Create mapping from element's ID to its xpath value
43
+ #
44
+ # @param [one of OpenNebula pool classes] pool_class pool to read elements from
45
+ # @param [String] xpath xpath pointing to value within the element
46
+ #
47
+ # @return [Hash] generated map
35
48
  def mapping(pool_class, xpath)
36
49
  @log.debug("Generating mapping for class: #{pool_class} and xpath: '#{xpath}'.")
37
50
  pool = pool_class.new(@client)
51
+ #call info_all method instead of info on pools that support it
38
52
  if pool.respond_to? 'info_all'
39
53
  rc = pool.info_all
40
54
  check_retval(rc, Errors::ResourceRetrievalError)
@@ -43,6 +57,7 @@ class OneDataAccessor
43
57
  check_retval(rc, Errors::ResourceRetrievalError)
44
58
  end
45
59
 
60
+ #generate mapping
46
61
  map = {}
47
62
  pool.each do |item|
48
63
  unless item['ID']
@@ -55,6 +70,11 @@ class OneDataAccessor
55
70
  map
56
71
  end
57
72
 
73
+ # Retrieve virtual machine
74
+ #
75
+ # @param [Integer] vm_id ID of vm to retrieve
76
+ #
77
+ # @return [OpenNebula::VirtualMachine] virtual machine
58
78
  def vm(vm_id)
59
79
  fail ArgumentError, "#{vm_id} is not a valid id." unless is_number?(vm_id)
60
80
  @log.debug("Retrieving virtual machine with id: #{vm_id}.")
@@ -64,8 +84,16 @@ class OneDataAccessor
64
84
  vm
65
85
  end
66
86
 
87
+ # Retriev IDs of specified virtual machines
88
+ #
89
+ # @param [Integer] batch_number
90
+ # @param [Hash] range date range into which virtual machine has to belong
91
+ # @param [Hash] groups groups into one of which owner of the virtual machine has to belong
92
+ #
93
+ # @return [Array] array with virtual machines' IDs
67
94
  def vms(batch_number, range, groups)
68
95
  vms = []
96
+ #load specific batch
69
97
  vm_pool = load_vm_pool(batch_number)
70
98
  return nil if vm_pool.count == 0
71
99
 
@@ -76,6 +104,7 @@ class OneDataAccessor
76
104
  next
77
105
  end
78
106
 
107
+ #skip unsuitable virtual machines
79
108
  next unless want?(vm, range, groups)
80
109
 
81
110
  vms << vm['ID'].to_i
@@ -85,6 +114,13 @@ class OneDataAccessor
85
114
  vms
86
115
  end
87
116
 
117
+ # Check whether obtained vm meets requierements
118
+ #
119
+ # @param [OpenNebula::VirtualMachine] vm virtual machine instance to check
120
+ # @param [Hash] range date range into which virtual machine has to belong
121
+ # @param [Hash] groups groups into one of which owner of the virtual machine has to belong
122
+ #
123
+ # @return [TrueClass, FalseClass] true if virtual machine meets requirements, false otherwise
88
124
  def want?(vm, range, groups)
89
125
  if vm.nil?
90
126
  @log.warn('Obtained nil vm from vm pool.')
@@ -105,12 +141,16 @@ class OneDataAccessor
105
141
  true
106
142
  end
107
143
 
144
+ # Load part of virtual machine pool
145
+ #
146
+ # @param [Integer] batch_number
108
147
  def load_vm_pool(batch_number)
109
148
  fail ArgumentError, "#{batch_number} is not a valid number" unless is_number?(batch_number)
110
149
  @log.debug("Loading vm pool with batch number: #{batch_number}.")
111
150
  from = batch_number * @batch_size
112
151
  to = (batch_number + 1) * @batch_size - 1
113
152
 
153
+ #if in compatibility mode, whole virtual machine pool has to be loaded for the first time
114
154
  if @compatibility
115
155
  unless @compatibility_vm_pool
116
156
  vm_pool = OpenNebula::VirtualMachinePool.new(@client)
@@ -129,6 +169,7 @@ class OneDataAccessor
129
169
  end
130
170
  end
131
171
 
172
+ # Check OpenNebula return codes
132
173
  def check_retval(rc, e_klass)
133
174
  return true unless OpenNebula.is_error?(rc)
134
175
  case rc.errno
data/lib/one_worker.rb CHANGED
@@ -8,6 +8,7 @@ require 'sidekiq_conf'
8
8
  require 'oneacct_exporter/log'
9
9
  require 'settings'
10
10
 
11
+ # Sidekiq worker class
11
12
  class OneWorker
12
13
  include Sidekiq::Worker
13
14
 
@@ -21,6 +22,7 @@ class OneWorker
21
22
  NON_ZERO = /[1-9][[:digit:]]*/
22
23
  STATES = %w(started started suspended started suspended suspended completed completed suspended)
23
24
 
25
+ # Prepare data that are common for every virtual machine
24
26
  def common_data
25
27
  common_data = {}
26
28
  common_data['endpoint'] = Settings['endpoint']
@@ -30,25 +32,35 @@ class OneWorker
30
32
  common_data
31
33
  end
32
34
 
35
+ # Create mapping of user ID and specified element
36
+ #
37
+ # @return [Hash] created map
33
38
  def create_user_map(oda)
34
39
  logger.debug('Creating user map.')
35
40
  create_map(OpenNebula::UserPool, 'TEMPLATE/X509_DN', oda)
36
41
  end
37
42
 
43
+ # Create mapping of image ID and specified element
44
+ #
45
+ # @return [Hash] created map
38
46
  def create_image_map(oda)
39
47
  logger.debug('Creating image map.')
40
- create_map(OpenNebula::ImagePool, 'NAME', oda)
48
+ create_map(OpenNebula::ImagePool, 'TEMPLATE/VMCATCHER_EVENT_AD_MPURI', oda)
41
49
  end
42
50
 
51
+ # Generic method for mapping creation
43
52
  def create_map(pool_type, mapping, oda)
44
53
  oda.mapping(pool_type, mapping)
45
54
  rescue => e
46
55
  msg = "Couldn't create map: #{e.message}. "\
47
- 'Stopping to avoid malformed records.'
56
+ 'Stopping to avoid malformed records.'
48
57
  logger.error(msg)
49
58
  raise msg
50
59
  end
51
60
 
61
+ # Load virtual machine with specified ID
62
+ #
63
+ # @return [OpenNebula::VirtualMachine] virtual machine
52
64
  def load_vm(vm_id, oda)
53
65
  oda.vm(vm_id)
54
66
  rescue => e
@@ -56,6 +68,9 @@ class OneWorker
56
68
  return nil
57
69
  end
58
70
 
71
+ # Obtain and parse required data from vm
72
+ #
73
+ # @return [Hash] required data from virtual machine
59
74
  def process_vm(vm, user_map, image_map)
60
75
  data = common_data.clone
61
76
 
@@ -66,16 +81,16 @@ class OneWorker
66
81
  return nil
67
82
  end
68
83
 
69
- data['start_time'] = parse(vm['STIME'], NUMBER)
84
+ data['start_time'] = Time.at(parse(vm['STIME'], NUMBER).to_i)
70
85
  start_time = data['start_time'].to_i
71
86
  if start_time == 0
72
87
  logger.error('Skipping a malformed record. '\
73
88
  "VM with id #{data['vm_uuid']} has malformed StartTime.")
74
89
  return nil
75
90
  end
76
- data['start_time_readable'] = parse(Time.at(start_time).strftime('%F %T%:z'), STRING)
77
91
  data['end_time'] = parse(vm['ETIME'], NON_ZERO)
78
92
  end_time = data['end_time'].to_i
93
+ data['end_time'] = Time.at(end_time) if end_time != 0
79
94
 
80
95
  if end_time != 0 && start_time > end_time
81
96
  logger.error('Skipping malformed record. '\
@@ -86,7 +101,8 @@ class OneWorker
86
101
  data['machine_name'] = parse(vm['DEPLOY_ID'], STRING, "one-#{data['vm_uuid']}")
87
102
  data['user_id'] = parse(vm['UID'], STRING)
88
103
  data['group_id'] = parse(vm['GID'], STRING)
89
- data['user_name'] = parse(user_map[data['user_id']], STRING)
104
+ data['user_name'] = parse(vm['USER_TEMPLATE/USER_X509_DN'], STRING, nil)
105
+ data['user_name'] = parse(user_map[data['user_id']], STRING) unless data['user_name']
90
106
  data['fqan'] = parse(vm['GNAME'], STRING, nil)
91
107
 
92
108
  if vm['STATE']
@@ -118,11 +134,37 @@ class OneWorker
118
134
  data['network_outbound'] = (net_rx.to_i / B_IN_GB).round
119
135
 
120
136
  data['memory'] = parse(vm['MEMORY'], NUMBER, '0')
121
- data['image_name'] = parse(image_map[vm['TEMPLATE/DISK[1]/IMAGE_ID']], STRING)
137
+
138
+ data['image_name'] = parse(image_map[vm['TEMPLATE/DISK[1]/IMAGE_ID']], STRING, nil)
139
+ data['image_name'] = parse(mixin(vm), STRING) unless data['image_name']
122
140
 
123
141
  data
124
142
  end
125
143
 
144
+ # Look for 'os_tpl' OCCI mixin to better identifie virtual machine's image
145
+ #
146
+ # @param [OpenNebula::VirtualMachine] vm virtual machine
147
+ #
148
+ # @return [NilClass, String] if found, mixin identifying string, nil otherwise
149
+ def mixin(vm)
150
+ mixin_locations = %w(USER_TEMPLATE/OCCI_COMPUTE_MIXINS USER_TEMPLATE/OCCI_MIXIN TEMPLATE/OCCI_MIXIN)
151
+
152
+ mixin_locations.each do |mixin_location|
153
+ vm.each mixin_location do |mixin|
154
+ mixin = mixin.text.split
155
+ mixin.select! { |line| line.include? '/occi/infrastructure/os_tpl#' }
156
+ return mixin.first unless mixin.empty?
157
+ end
158
+ end
159
+
160
+ nil # nothing found
161
+ end
162
+
163
+ # Sums RSTIME (time when virtual machine was actually running)
164
+ #
165
+ # @param [OpenNebula::VirtualMachine] vm virtual machine
166
+ #
167
+ # @return [Integer] RSTIME
126
168
  def sum_rstime(vm)
127
169
  rstime = 0
128
170
  vm.each 'HISTORY_RECORDS/HISTORY' do |h|
@@ -139,6 +181,10 @@ class OneWorker
139
181
  rstime
140
182
  end
141
183
 
184
+ # Sidekiq specific method, specifies the purpose of the worker
185
+ #
186
+ # @param [String] vms IDs of virtual machines to process in form of numbers separated by '|' (easier for cooperation with redis)
187
+ # @param [String] output output directory
142
188
  def perform(vms, output)
143
189
  OneacctExporter::Log.setup_log_level(logger)
144
190
 
@@ -165,12 +211,13 @@ class OneWorker
165
211
  write_data(data, output)
166
212
  end
167
213
 
214
+ # Write processed data into output directory
168
215
  def write_data(data, output)
169
216
  logger.debug('Creating writer...')
170
217
  ow = OneWriter.new(data, output, logger)
171
218
  ow.write
172
219
  rescue => e
173
- msg = "Canno't write result to #{output}: #{e.message}"
220
+ msg = "Cannot write result to #{output}: #{e.message}"
174
221
  logger.error(msg)
175
222
  raise msg
176
223
  end
data/lib/one_writer.rb CHANGED
@@ -4,8 +4,15 @@ require 'fileutils'
4
4
  require 'settings'
5
5
  require 'logger'
6
6
 
7
+ # Class responsible for writing data into files in specific format
8
+ #
9
+ # @attr_reader [Hash] data vm data
10
+ # @attr_reader [String] output path to the output directory
11
+ # @attr_reader [any logger] logger
7
12
  class OneWriter
13
+
8
14
  attr_reader :data, :output, :log
15
+
9
16
  def initialize(data, output, log = Logger.new(STDOUT))
10
17
  fail ArgumentError, 'Data and output cannot be nil' if data.nil? || output.nil?
11
18
 
@@ -17,6 +24,7 @@ class OneWriter
17
24
  @log = log
18
25
  end
19
26
 
27
+ # Write data to file in output directory
20
28
  def write
21
29
  @log.debug('Creating temporary file...')
22
30
  tmp = Tempfile.new('oneacct_export')
@@ -38,6 +46,9 @@ class OneWriter
38
46
  FileUtils.cp(from, to)
39
47
  end
40
48
 
49
+ # Prepare file content according to ERB template
50
+ #
51
+ # @return [String] transformed content
41
52
  def fill_template
42
53
  @log.debug("Reading erb template from file: '#{@template}'.")
43
54
  erb = ERB.new(File.read(@template), nil, '-')
@@ -45,6 +56,9 @@ class OneWriter
45
56
  erb.result(binding)
46
57
  end
47
58
 
59
+ # Load template for data conversion
60
+ #
61
+ # @param [String] template_name name of the template to look for
48
62
  def self.template_filename(template_name)
49
63
  "#{File.dirname(__FILE__)}/templates/#{template_name}.erb"
50
64
  end
@@ -4,6 +4,14 @@ require 'one_worker'
4
4
  require 'settings'
5
5
  require 'sidekiq/api'
6
6
 
7
+ # Class managing the export
8
+ #
9
+ # @attr_reader [any logger] log logger for the class
10
+ # @attr_reader [Hash] range range of dates, requesting only virtual machines within the range
11
+ # @attr_reader [Hash] groups user groups, requesting only virtual machines with owners that belong to one of the group
12
+ # @attr_reader [TrueClass, FalseClass] blocking says whether to run export in blocking mode or not
13
+ # @attr_reader [Integer] timeout timeout for blocking mode
14
+ # @attr_reader [TrueClass, FalseClass] compatibility says whether to run export in compatibility mode or not
7
15
  class OneacctExporter
8
16
  CONVERT_FORMAT = '%014d'
9
17
 
@@ -18,6 +26,7 @@ class OneacctExporter
18
26
  @compatibility = options[:compatibility]
19
27
  end
20
28
 
29
+ # Start export the records
21
30
  def export
22
31
  @log.debug('Starting export...')
23
32
 
@@ -28,10 +37,12 @@ class OneacctExporter
28
37
  oda = OneDataAccessor.new(@compatibility, @log)
29
38
 
30
39
  vms = []
40
+ #load records of virtual machines in batches
31
41
  while vms = oda.vms(batch_number, @range, @groups)
32
42
  output_file = CONVERT_FORMAT % new_file_number
33
43
  @log.info("Starting worker with batch number: #{batch_number}.")
34
44
  unless vms.empty?
45
+ #add a new job for every batch to the Sidekiq's queue
35
46
  OneWorker.perform_async(vms.join('|'), "#{Settings.output['output_dir']}/#{output_file}")
36
47
  new_file_number += 1
37
48
  end
@@ -50,6 +61,7 @@ class OneacctExporter
50
61
  "failed with error: #{e.message}. Exiting.")
51
62
  end
52
63
 
64
+ # When in blocking mode, wait for processing of records to finish
53
65
  def wait_for_processing
54
66
  @log.info('Processing...')
55
67
 
@@ -66,6 +78,7 @@ class OneacctExporter
66
78
  @log.info('All processing ended.')
67
79
  end
68
80
 
81
+ # Check whether Sidekiq's queue is empty
69
82
  def queue_empty?
70
83
  queue = (Settings['sidekiq'] && Settings.sidekiq['queue']) ? Settings.sidekiq['queue'] : 'default'
71
84
  Sidekiq::Stats.new.queues.each_pair do |queue_name, items_in_queue|
@@ -75,10 +88,12 @@ class OneacctExporter
75
88
  true
76
89
  end
77
90
 
91
+ # Check whether all Sidekiq workers have finished thair work
78
92
  def all_workers_done?
79
93
  Sidekiq::Workers.new.size == 0
80
94
  end
81
95
 
96
+ # Clean output directory of previous entries
82
97
  def clean_output_dir
83
98
  output_dir = Dir.new(Settings.output['output_dir'])
84
99
  output_dir.entries.each do |entry|
@@ -1,3 +1,3 @@
1
1
  class OneacctExporter
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/oneacct_opts.rb CHANGED
@@ -4,6 +4,7 @@ require 'ostruct'
4
4
  require 'oneacct_exporter'
5
5
  require 'settings'
6
6
 
7
+ # Class for parsing command line arguments
7
8
  class OneacctOpts
8
9
  BLOCKING_DEFAULT = false
9
10
  TIMEOUT_DEFAULT = 60 * 60
@@ -78,6 +79,7 @@ class OneacctOpts
78
79
  options
79
80
  end
80
81
 
82
+ # Set default values for not specified options
81
83
  def self.set_defaults(options)
82
84
  options.blocking = BLOCKING_DEFAULT unless options.blocking
83
85
  unless options.timeout
@@ -91,38 +93,47 @@ class OneacctOpts
91
93
  check_settings_restrictions
92
94
  end
93
95
 
96
+ # Make sure command line parameters are sane
94
97
  def self.check_options_restrictions(options)
98
+ #make sure date range make sense
95
99
  if options.records_from && options.records_to && options.records_from >= options.records_to
96
100
  fail ArgumentError, 'Wrong time range for records retrieval.'
97
101
  end
98
102
 
103
+ #make sure only one group restriction is used
99
104
  if options.include_groups && options.exclude_groups
100
105
  fail ArgumentError, 'Mixing of group options is not possible.'
101
106
  end
102
107
 
108
+ #make sure group file option is not used without specifying group restriction type
103
109
  unless options.include_groups || options.exclude_groups
104
110
  if options.groups_file
105
111
  fail ArgumentError, 'Cannot use group file without specifying group restriction type.'
106
112
  end
107
113
  end
108
114
 
115
+ #make sure that timeout option is not used without blocking option
109
116
  if options.timeout && !options.blocking
110
117
  fail ArgumentError, 'Cannot set timeout without a blocking mode.'
111
118
  end
112
119
  end
113
120
 
121
+ # Make sure configuration is sane
114
122
  def self.check_settings_restrictions
123
+ #make sure all mandatory parameters are set
115
124
  unless Settings['site_name'] && Settings['cloud_type'] && Settings['endpoint'] &&
116
125
  Settings['output'] && Settings.output['output_dir'] && Settings.output['output_type']
117
126
  fail ArgumentError, 'Missing some mandatory parameters. Check your configuration file.'
118
127
  end
119
128
  Settings['endpoint'].chop! if Settings['endpoint'].end_with?('/')
120
129
 
130
+ #make sure log file is specified while loggin to file
121
131
  if Settings['logging'] && Settings.logging['log_type'] == 'file' &&
122
132
  !Settings.logging['log_file']
123
133
  fail ArgumentError, 'Missing file for logging. Check your configuration file.'
124
134
  end
125
135
 
136
+ #make sure specified template really exists
126
137
  template_filename = OneWriter.template_filename(Settings.output['output_type'])
127
138
  unless File.exist?(template_filename)
128
139
  fail ArgumentError, "Non-existing template #{Settings.output['output_type']}."
data/lib/redis_conf.rb CHANGED
@@ -2,9 +2,13 @@ require 'settings'
2
2
  require 'uri'
3
3
  require 'input_validator'
4
4
 
5
+ # Class that deals with Redis server configuration options
5
6
  class RedisConf
6
7
  extend InputValidator
7
8
 
9
+ # Read and parse Redis server configuration options
10
+ #
11
+ # @return [Hash] redis server options ready for use
8
12
  def self.options
9
13
  options = {}
10
14
  if Settings['redis']
data/lib/settings.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'settingslogic'
2
2
 
3
+ # Class representing OneacctExport settings
3
4
  class Settings < Settingslogic
4
5
  CONF_NAME = 'conf.yml'
5
6
 
7
+ #three possible configuration file locations in order by preference
8
+ #if configuration file is found rest of the locations are ignored
6
9
  source "#{ENV['HOME']}/.oneacct-export/#{CONF_NAME}"\
7
10
  if File.exist?("#{ENV['HOME']}/.oneacct-export/#{CONF_NAME}")
8
11
  source "/etc/oneacct-export/#{CONF_NAME}"\
data/lib/sidekiq_conf.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'sidekiq'
2
2
  require 'redis_conf'
3
3
 
4
+ # Class representing Sidekiq configuration
4
5
  Sidekiq.configure_client do |config|
5
6
  options = RedisConf.options
6
7
  options[:size] = 1
@@ -1,6 +1,6 @@
1
1
  APEL-cloud-message: v0.2
2
2
  <% for vm in @data -%>
3
- VMUUID: <%= vm['endpoint'] %>/compute/<%= vm['vm_uuid'] %> <%= vm['start_time_readable']%>
3
+ VMUUID: <%= vm['endpoint'] %>/compute/<%= vm['vm_uuid'] %> <%= vm['start_time'].strftime('%F %T%:z') %>
4
4
  SiteName: <%= vm['site_name']%>
5
5
  MachineName: <%= vm['machine_name']%>
6
6
  LocalUserId: <%= vm['user_id']%>
@@ -12,8 +12,8 @@ FQAN: /<%= vm['fqan']%>/Role=NULL/Capability=NULL
12
12
  FQAN: NULL
13
13
  <% end -%>
14
14
  Status: <%= vm['status']%>
15
- StartTime: <%= vm['start_time']%>
16
- EndTime: <%= vm['end_time']%>
15
+ StartTime: <%= vm['start_time'].to_i%>
16
+ EndTime: <%= vm['end_time'].to_i%>
17
17
  SuspendDuration: <%= vm['suspend']%>
18
18
  WallDuration: <%= vm['duration']%>
19
19
  CpuDuration: <%= vm['duration']%>
@@ -0,0 +1,106 @@
1
+ <VM>
2
+ <ID>46083</ID>
3
+ <UID>120</UID>
4
+ <GID>103</GID>
5
+ <UNAME>local_ops</UNAME>
6
+ <GNAME>ops</GNAME>
7
+ <NAME>EGI-SAM</NAME>
8
+ <PERMISSIONS>
9
+ <OWNER_U>1</OWNER_U>
10
+ <OWNER_M>1</OWNER_M>
11
+ <OWNER_A>0</OWNER_A>
12
+ <GROUP_U>0</GROUP_U>
13
+ <GROUP_M>0</GROUP_M>
14
+ <GROUP_A>0</GROUP_A>
15
+ <OTHER_U>0</OTHER_U>
16
+ <OTHER_M>0</OTHER_M>
17
+ <OTHER_A>0</OTHER_A>
18
+ </PERMISSIONS>
19
+ <LAST_POLL>1414588018</LAST_POLL>
20
+ <STATE>3</STATE>
21
+ <LCM_STATE>12</LCM_STATE>
22
+ <RESCHED>0</RESCHED>
23
+ <STIME>1414587675</STIME>
24
+ <ETIME>0</ETIME>
25
+ <DEPLOY_ID>one-46083</DEPLOY_ID>
26
+ <MEMORY>2097152</MEMORY>
27
+ <CPU>0</CPU>
28
+ <NET_TX>3072</NET_TX>
29
+ <NET_RX>0</NET_RX>
30
+ <TEMPLATE>
31
+ <AUTOMATIC_REQUIREMENTS><![CDATA[CLUSTER_ID = 100 & !(PUBLIC_CLOUD = YES)]]></AUTOMATIC_REQUIREMENTS>
32
+ <CPU><![CDATA[1]]></CPU>
33
+ <DISK>
34
+ <BUS><![CDATA[ide]]></BUS>
35
+ <CLONE><![CDATA[YES]]></CLONE>
36
+ <CLONE_TARGET><![CDATA[SYSTEM]]></CLONE_TARGET>
37
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
38
+ <DATASTORE><![CDATA[default]]></DATASTORE>
39
+ <DATASTORE_ID><![CDATA[1]]></DATASTORE_ID>
40
+ <DEV_PREFIX><![CDATA[xvd]]></DEV_PREFIX>
41
+ <DISK_ID><![CDATA[0]]></DISK_ID>
42
+ <DRIVER><![CDATA[tap2:tapdisk:aio:]]></DRIVER>
43
+ <IMAGE><![CDATA[monitoring]]></IMAGE>
44
+ <IMAGE_ID><![CDATA[31]]></IMAGE_ID>
45
+ <LN_TARGET><![CDATA[SYSTEM]]></LN_TARGET>
46
+ <READONLY><![CDATA[NO]]></READONLY>
47
+ <SAVE><![CDATA[NO]]></SAVE>
48
+ <SIZE><![CDATA[106]]></SIZE>
49
+ <SOURCE><![CDATA[/opt/opennebula/var/datastores/1/a8e87be2fdafadadd5575d73d4946d47]]></SOURCE>
50
+ <TARGET><![CDATA[xvda]]></TARGET>
51
+ <TM_MAD><![CDATA[ssh]]></TM_MAD>
52
+ <TYPE><![CDATA[FILE]]></TYPE>
53
+ </DISK>
54
+ <MEMORY><![CDATA[2048]]></MEMORY>
55
+ <NAME><![CDATA[one-46083]]></NAME>
56
+ <NIC>
57
+ <BRIDGE><![CDATA[xenbr0]]></BRIDGE>
58
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
59
+ <IP><![CDATA[192.168.254.26]]></IP>
60
+ <IP6_LINK><![CDATA[fe80::400:c0ff:fea8:fe1a]]></IP6_LINK>
61
+ <MAC><![CDATA[02:00:c0:a8:fe:1a]]></MAC>
62
+ <NETWORK><![CDATA[monitoring]]></NETWORK>
63
+ <NETWORK_ID><![CDATA[6]]></NETWORK_ID>
64
+ <NIC_ID><![CDATA[0]]></NIC_ID>
65
+ <VLAN><![CDATA[NO]]></VLAN>
66
+ </NIC>
67
+ <OS>
68
+ <BOOTLOADER><![CDATA[pygrub]]></BOOTLOADER>
69
+ </OS>
70
+ <RAW>
71
+ <TYPE><![CDATA[xen]]></TYPE>
72
+ </RAW>
73
+ <VCPU><![CDATA[1]]></VCPU>
74
+ <VMID><![CDATA[46083]]></VMID>
75
+ </TEMPLATE>
76
+ <USER_TEMPLATE>
77
+ <ARCHITECTURE><![CDATA[x86]]></ARCHITECTURE>
78
+ <DESCRIPTION><![CDATA[Instantiated with rOCCI-server on Wed, 29 Oct 2014 14:01:15 +0100.]]></DESCRIPTION>
79
+ <OCCI_COMPUTE_MIXINS><![CDATA[http://occi.localhost/occi/infrastructure/os_tpl#uuid_monitoring_20 http://schema.fedcloud.egi.eu/occi/infrastructure/resource_tpl#small http://opennebula.org/occi/infrastructure#compute]]></OCCI_COMPUTE_MIXINS>
80
+ <OCCI_ID><![CDATA[46083]]></OCCI_ID>
81
+ </USER_TEMPLATE>
82
+ <HISTORY_RECORDS>
83
+ <HISTORY>
84
+ <OID>46083</OID>
85
+ <SEQ>0</SEQ>
86
+ <HOSTNAME>localhost</HOSTNAME>
87
+ <HID>17</HID>
88
+ <CID>100</CID>
89
+ <STIME>1414587683</STIME>
90
+ <ETIME>0</ETIME>
91
+ <VMMMAD>xen</VMMMAD>
92
+ <VNMMAD>802.1Q</VNMMAD>
93
+ <TMMAD>ssh</TMMAD>
94
+ <DS_LOCATION>/opt/opennebula/var/datastores</DS_LOCATION>
95
+ <DS_ID>0</DS_ID>
96
+ <PSTIME>1414587683</PSTIME>
97
+ <PETIME>1414587685</PETIME>
98
+ <RSTIME>1414587685</RSTIME>
99
+ <RETIME>0</RETIME>
100
+ <ESTIME>0</ESTIME>
101
+ <EETIME>0</EETIME>
102
+ <REASON>0</REASON>
103
+ <ACTION>3</ACTION>
104
+ </HISTORY>
105
+ </HISTORY_RECORDS>
106
+ </VM>
@@ -0,0 +1,106 @@
1
+ <VM>
2
+ <ID>46083</ID>
3
+ <UID>120</UID>
4
+ <GID>103</GID>
5
+ <UNAME>local_ops</UNAME>
6
+ <GNAME>ops</GNAME>
7
+ <NAME>EGI-SAM</NAME>
8
+ <PERMISSIONS>
9
+ <OWNER_U>1</OWNER_U>
10
+ <OWNER_M>1</OWNER_M>
11
+ <OWNER_A>0</OWNER_A>
12
+ <GROUP_U>0</GROUP_U>
13
+ <GROUP_M>0</GROUP_M>
14
+ <GROUP_A>0</GROUP_A>
15
+ <OTHER_U>0</OTHER_U>
16
+ <OTHER_M>0</OTHER_M>
17
+ <OTHER_A>0</OTHER_A>
18
+ </PERMISSIONS>
19
+ <LAST_POLL>1414588018</LAST_POLL>
20
+ <STATE>3</STATE>
21
+ <LCM_STATE>12</LCM_STATE>
22
+ <RESCHED>0</RESCHED>
23
+ <STIME>1414587675</STIME>
24
+ <ETIME>0</ETIME>
25
+ <DEPLOY_ID>one-46083</DEPLOY_ID>
26
+ <MEMORY>2097152</MEMORY>
27
+ <CPU>0</CPU>
28
+ <NET_TX>3072</NET_TX>
29
+ <NET_RX>0</NET_RX>
30
+ <TEMPLATE>
31
+ <AUTOMATIC_REQUIREMENTS><![CDATA[CLUSTER_ID = 100 & !(PUBLIC_CLOUD = YES)]]></AUTOMATIC_REQUIREMENTS>
32
+ <CPU><![CDATA[1]]></CPU>
33
+ <DISK>
34
+ <BUS><![CDATA[ide]]></BUS>
35
+ <CLONE><![CDATA[YES]]></CLONE>
36
+ <CLONE_TARGET><![CDATA[SYSTEM]]></CLONE_TARGET>
37
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
38
+ <DATASTORE><![CDATA[default]]></DATASTORE>
39
+ <DATASTORE_ID><![CDATA[1]]></DATASTORE_ID>
40
+ <DEV_PREFIX><![CDATA[xvd]]></DEV_PREFIX>
41
+ <DISK_ID><![CDATA[0]]></DISK_ID>
42
+ <DRIVER><![CDATA[tap2:tapdisk:aio:]]></DRIVER>
43
+ <IMAGE><![CDATA[monitoring]]></IMAGE>
44
+ <IMAGE_ID><![CDATA[31]]></IMAGE_ID>
45
+ <LN_TARGET><![CDATA[SYSTEM]]></LN_TARGET>
46
+ <READONLY><![CDATA[NO]]></READONLY>
47
+ <SAVE><![CDATA[NO]]></SAVE>
48
+ <SIZE><![CDATA[106]]></SIZE>
49
+ <SOURCE><![CDATA[/opt/opennebula/var/datastores/1/a8e87be2fdafadadd5575d73d4946d47]]></SOURCE>
50
+ <TARGET><![CDATA[xvda]]></TARGET>
51
+ <TM_MAD><![CDATA[ssh]]></TM_MAD>
52
+ <TYPE><![CDATA[FILE]]></TYPE>
53
+ </DISK>
54
+ <MEMORY><![CDATA[2048]]></MEMORY>
55
+ <NAME><![CDATA[one-46083]]></NAME>
56
+ <NIC>
57
+ <BRIDGE><![CDATA[xenbr0]]></BRIDGE>
58
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
59
+ <IP><![CDATA[192.168.254.26]]></IP>
60
+ <IP6_LINK><![CDATA[fe80::400:c0ff:fea8:fe1a]]></IP6_LINK>
61
+ <MAC><![CDATA[02:00:c0:a8:fe:1a]]></MAC>
62
+ <NETWORK><![CDATA[monitoring]]></NETWORK>
63
+ <NETWORK_ID><![CDATA[6]]></NETWORK_ID>
64
+ <NIC_ID><![CDATA[0]]></NIC_ID>
65
+ <VLAN><![CDATA[NO]]></VLAN>
66
+ </NIC>
67
+ <OS>
68
+ <BOOTLOADER><![CDATA[pygrub]]></BOOTLOADER>
69
+ </OS>
70
+ <RAW>
71
+ <TYPE><![CDATA[xen]]></TYPE>
72
+ </RAW>
73
+ <VCPU><![CDATA[1]]></VCPU>
74
+ <VMID><![CDATA[46083]]></VMID>
75
+ </TEMPLATE>
76
+ <USER_TEMPLATE>
77
+ <ARCHITECTURE><![CDATA[x86]]></ARCHITECTURE>
78
+ <OCCI_ID><![CDATA[4040029c-9c13-4b29-9d01-1deb179ee925]]></OCCI_ID>
79
+ <OCCI_MIXIN><![CDATA[https://occi.localhost/occi/infrastructure/resource_tpl#small]]></OCCI_MIXIN>
80
+ <OCCI_MIXIN><![CDATA[https://occi.localhost/occi/infrastructure/os_tpl#omr_worker_x86_64_ide_1_0]]></OCCI_MIXIN>
81
+ </USER_TEMPLATE>
82
+ <HISTORY_RECORDS>
83
+ <HISTORY>
84
+ <OID>46083</OID>
85
+ <SEQ>0</SEQ>
86
+ <HOSTNAME>localhost</HOSTNAME>
87
+ <HID>17</HID>
88
+ <CID>100</CID>
89
+ <STIME>1414587683</STIME>
90
+ <ETIME>0</ETIME>
91
+ <VMMMAD>xen</VMMMAD>
92
+ <VNMMAD>802.1Q</VNMMAD>
93
+ <TMMAD>ssh</TMMAD>
94
+ <DS_LOCATION>/opt/opennebula/var/datastores</DS_LOCATION>
95
+ <DS_ID>0</DS_ID>
96
+ <PSTIME>1414587683</PSTIME>
97
+ <PETIME>1414587685</PETIME>
98
+ <RSTIME>1414587685</RSTIME>
99
+ <RETIME>0</RETIME>
100
+ <ESTIME>0</ESTIME>
101
+ <EETIME>0</EETIME>
102
+ <REASON>0</REASON>
103
+ <ACTION>3</ACTION>
104
+ </HISTORY>
105
+ </HISTORY_RECORDS>
106
+ </VM>
@@ -0,0 +1,107 @@
1
+ <VM>
2
+ <ID>46083</ID>
3
+ <UID>120</UID>
4
+ <GID>103</GID>
5
+ <UNAME>local_ops</UNAME>
6
+ <GNAME>ops</GNAME>
7
+ <NAME>EGI-SAM</NAME>
8
+ <PERMISSIONS>
9
+ <OWNER_U>1</OWNER_U>
10
+ <OWNER_M>1</OWNER_M>
11
+ <OWNER_A>0</OWNER_A>
12
+ <GROUP_U>0</GROUP_U>
13
+ <GROUP_M>0</GROUP_M>
14
+ <GROUP_A>0</GROUP_A>
15
+ <OTHER_U>0</OTHER_U>
16
+ <OTHER_M>0</OTHER_M>
17
+ <OTHER_A>0</OTHER_A>
18
+ </PERMISSIONS>
19
+ <LAST_POLL>1414588018</LAST_POLL>
20
+ <STATE>3</STATE>
21
+ <LCM_STATE>12</LCM_STATE>
22
+ <RESCHED>0</RESCHED>
23
+ <STIME>1414587675</STIME>
24
+ <ETIME>0</ETIME>
25
+ <DEPLOY_ID>one-46083</DEPLOY_ID>
26
+ <MEMORY>2097152</MEMORY>
27
+ <CPU>0</CPU>
28
+ <NET_TX>3072</NET_TX>
29
+ <NET_RX>0</NET_RX>
30
+ <TEMPLATE>
31
+ <AUTOMATIC_REQUIREMENTS><![CDATA[CLUSTER_ID = 100 & !(PUBLIC_CLOUD = YES)]]></AUTOMATIC_REQUIREMENTS>
32
+ <CPU><![CDATA[1]]></CPU>
33
+ <DISK>
34
+ <BUS><![CDATA[ide]]></BUS>
35
+ <CLONE><![CDATA[YES]]></CLONE>
36
+ <CLONE_TARGET><![CDATA[SYSTEM]]></CLONE_TARGET>
37
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
38
+ <DATASTORE><![CDATA[default]]></DATASTORE>
39
+ <DATASTORE_ID><![CDATA[1]]></DATASTORE_ID>
40
+ <DEV_PREFIX><![CDATA[xvd]]></DEV_PREFIX>
41
+ <DISK_ID><![CDATA[0]]></DISK_ID>
42
+ <DRIVER><![CDATA[tap2:tapdisk:aio:]]></DRIVER>
43
+ <IMAGE><![CDATA[monitoring]]></IMAGE>
44
+ <IMAGE_ID><![CDATA[31]]></IMAGE_ID>
45
+ <LN_TARGET><![CDATA[SYSTEM]]></LN_TARGET>
46
+ <READONLY><![CDATA[NO]]></READONLY>
47
+ <SAVE><![CDATA[NO]]></SAVE>
48
+ <SIZE><![CDATA[106]]></SIZE>
49
+ <SOURCE><![CDATA[/opt/opennebula/var/datastores/1/a8e87be2fdafadadd5575d73d4946d47]]></SOURCE>
50
+ <TARGET><![CDATA[xvda]]></TARGET>
51
+ <TM_MAD><![CDATA[ssh]]></TM_MAD>
52
+ <TYPE><![CDATA[FILE]]></TYPE>
53
+ </DISK>
54
+ <MEMORY><![CDATA[2048]]></MEMORY>
55
+ <NAME><![CDATA[one-46083]]></NAME>
56
+ <NIC>
57
+ <BRIDGE><![CDATA[xenbr0]]></BRIDGE>
58
+ <CLUSTER_ID><![CDATA[100]]></CLUSTER_ID>
59
+ <IP><![CDATA[192.168.254.26]]></IP>
60
+ <IP6_LINK><![CDATA[fe80::400:c0ff:fea8:fe1a]]></IP6_LINK>
61
+ <MAC><![CDATA[02:00:c0:a8:fe:1a]]></MAC>
62
+ <NETWORK><![CDATA[monitoring]]></NETWORK>
63
+ <NETWORK_ID><![CDATA[6]]></NETWORK_ID>
64
+ <NIC_ID><![CDATA[0]]></NIC_ID>
65
+ <VLAN><![CDATA[NO]]></VLAN>
66
+ </NIC>
67
+ <OS>
68
+ <BOOTLOADER><![CDATA[pygrub]]></BOOTLOADER>
69
+ </OS>
70
+ <RAW>
71
+ <TYPE><![CDATA[xen]]></TYPE>
72
+ </RAW>
73
+ <VCPU><![CDATA[1]]></VCPU>
74
+ <VMID><![CDATA[46083]]></VMID>
75
+ </TEMPLATE>
76
+ <USER_TEMPLATE>
77
+ <ARCHITECTURE><![CDATA[x86]]></ARCHITECTURE>
78
+ <DESCRIPTION><![CDATA[Instantiated with rOCCI-server on Wed, 29 Oct 2014 14:01:15 +0100.]]></DESCRIPTION>
79
+ <OCCI_COMPUTE_MIXINS><![CDATA[http://occi.localhost/occi/infrastructure/os_tpl#uuid_monitoring_20 http://schema.fedcloud.egi.eu/occi/infrastructure/resource_tpl#small http://opennebula.org/occi/infrastructure#compute]]></OCCI_COMPUTE_MIXINS>
80
+ <OCCI_ID><![CDATA[46083]]></OCCI_ID>
81
+ <USER_X509_DN><![CDATA[/MY=STuPID/CN=DN/CN=HERE]]></USER_X509_DN>
82
+ </USER_TEMPLATE>
83
+ <HISTORY_RECORDS>
84
+ <HISTORY>
85
+ <OID>46083</OID>
86
+ <SEQ>0</SEQ>
87
+ <HOSTNAME>localhost</HOSTNAME>
88
+ <HID>17</HID>
89
+ <CID>100</CID>
90
+ <STIME>1414587683</STIME>
91
+ <ETIME>0</ETIME>
92
+ <VMMMAD>xen</VMMMAD>
93
+ <VNMMAD>802.1Q</VNMMAD>
94
+ <TMMAD>ssh</TMMAD>
95
+ <DS_LOCATION>/opt/opennebula/var/datastores</DS_LOCATION>
96
+ <DS_ID>0</DS_ID>
97
+ <PSTIME>1414587683</PSTIME>
98
+ <PETIME>1414587685</PETIME>
99
+ <RSTIME>1414587685</RSTIME>
100
+ <RETIME>0</RETIME>
101
+ <ESTIME>0</ESTIME>
102
+ <EETIME>0</EETIME>
103
+ <REASON>0</REASON>
104
+ <ACTION>3</ACTION>
105
+ </HISTORY>
106
+ </HISTORY_RECORDS>
107
+ </VM>
@@ -13,10 +13,11 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = ''
14
14
  spec.license = 'MIT'
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ['lib']
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ spec.required_ruby_version = '>= 2.0.0'
20
21
 
21
22
  spec.add_development_dependency 'bundler', '~> 1.6'
22
23
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -26,6 +27,6 @@ Gem::Specification.new do |spec|
26
27
 
27
28
  spec.add_runtime_dependency 'opennebula', '~> 4.6.0'
28
29
  spec.add_runtime_dependency 'syslogger', '~> 1.6.0'
29
- spec.add_runtime_dependency 'sidekiq', '= 3.2.0'
30
+ spec.add_runtime_dependency 'sidekiq', '~> 3.2.6'
30
31
  spec.add_runtime_dependency 'settingslogic', '~> 2.0.9'
31
32
  end
@@ -163,9 +163,8 @@ describe OneWorker do
163
163
  data['site_name'] = 'Hogwarts'
164
164
  data['cloud_type'] = 'OpenNebula'
165
165
  data['vm_uuid'] = '36551'
166
- data['start_time'] = '1383741160'
167
- data['start_time_readable'] = '2013-11-06 13:32:40+01:00'
168
- data['end_time'] = '1383742270'
166
+ data['start_time'] = Time.at(1383741160)
167
+ data['end_time'] = Time.at(1383742270)
169
168
  data['machine_name'] = 'one-36551'
170
169
  data['user_id'] = '120'
171
170
  data['group_id'] = '0'
@@ -535,6 +534,45 @@ describe OneWorker do
535
534
  expect(subject.process_vm(vm, user_map, image_map)).to eq(data)
536
535
  end
537
536
  end
537
+
538
+ context 'vm with USER_TEMPLATE/OCCI_COMPUTE_MIXINS' do
539
+ let(:filename) { 'one_worker_vm4.xml' }
540
+ let(:image_name) { 'http://occi.localhost/occi/infrastructure/os_tpl#uuid_monitoring_20' }
541
+
542
+ it 'w/o map info uses os_tpl mixin' do
543
+ expect(subject.process_vm(vm, user_map, {})['image_name']).to eq(image_name)
544
+ end
545
+
546
+ it 'w/ map info uses map info' do
547
+ expect(subject.process_vm(vm, user_map, image_map)['image_name']).to eq(data['image_name'])
548
+ end
549
+ end
550
+
551
+ context 'vm with USER_TEMPLATE/OCCI_MIXIN' do
552
+ let(:filename) { 'one_worker_vm5.xml' }
553
+ let(:image_name) { 'https://occi.localhost/occi/infrastructure/os_tpl#omr_worker_x86_64_ide_1_0' }
554
+
555
+ it 'w/o map info uses os_tpl mixin' do
556
+ expect(subject.process_vm(vm, user_map, {})['image_name']).to eq(image_name)
557
+ end
558
+
559
+ it 'w/ map info uses map info' do
560
+ expect(subject.process_vm(vm, user_map, image_map)['image_name']).to eq(data['image_name'])
561
+ end
562
+ end
563
+
564
+ context 'vm with USER_TEMPLATE/USER_X509_DN' do
565
+ let(:filename) { 'one_worker_vm6.xml' }
566
+ let(:user_name) { '/MY=STuPID/CN=DN/CN=HERE' }
567
+
568
+ it 'w/o map info uses USER_X509_DN' do
569
+ expect(subject.process_vm(vm, user_map, {})['user_name']).to eq(user_name)
570
+ end
571
+
572
+ it 'w/ map info uses USER_X509_DN' do
573
+ expect(subject.process_vm(vm, user_map, image_map)['user_name']).to eq(user_name)
574
+ end
575
+ end
538
576
  end
539
577
 
540
578
  describe '.sum_rstime' do
@@ -617,9 +655,8 @@ describe OneWorker do
617
655
  data['site_name'] = 'Hogwarts'
618
656
  data['cloud_type'] = 'OpenNebula'
619
657
  data['vm_uuid'] = '36551'
620
- data['start_time'] = '1383741160'
621
- data['start_time_readable'] = '2013-11-06 13:32:40+01:00'
622
- data['end_time'] = '1383742270'
658
+ data['start_time'] = Time.at(1383741160)
659
+ data['end_time'] = Time.at(1383742270)
623
660
  data['machine_name'] = 'one-36551'
624
661
  data['user_id'] = '120'
625
662
  data['group_id'] = '0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oneacct-export
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Kimle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-07 00:00:00.000000000 Z
11
+ date: 2014-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -112,16 +112,16 @@ dependencies:
112
112
  name: sidekiq
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '='
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 3.2.0
117
+ version: 3.2.6
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '='
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 3.2.0
124
+ version: 3.2.6
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: settingslogic
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +145,8 @@ extensions: []
145
145
  extra_rdoc_files: []
146
146
  files:
147
147
  - ".gitignore"
148
+ - ".travis.yml"
149
+ - ".yardopts"
148
150
  - Gemfile
149
151
  - LICENSE.txt
150
152
  - README.md
@@ -211,6 +213,9 @@ files:
211
213
  - mock/one_worker_vm1.xml
212
214
  - mock/one_worker_vm2.xml
213
215
  - mock/one_worker_vm3.xml
216
+ - mock/one_worker_vm4.xml
217
+ - mock/one_worker_vm5.xml
218
+ - mock/one_worker_vm6.xml
214
219
  - mock/one_writer_testfile
215
220
  - oneacct-export.gemspec
216
221
  - spec/one_data_accessor_spec.rb
@@ -232,7 +237,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
232
237
  requirements:
233
238
  - - ">="
234
239
  - !ruby/object:Gem::Version
235
- version: '0'
240
+ version: 2.0.0
236
241
  required_rubygems_version: !ruby/object:Gem::Requirement
237
242
  requirements:
238
243
  - - ">="
@@ -252,3 +257,4 @@ test_files:
252
257
  - spec/oneacct_opts_spec.rb
253
258
  - spec/redis_conf_spec.rb
254
259
  - spec/spec_helper.rb
260
+ has_rdoc: