lmc 0.8.0 → 0.12.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.
@@ -5,12 +5,11 @@ module LMC
5
5
  attr_reader :actual, :outdated
6
6
 
7
7
  def initialize(data)
8
- @actual = data["ACTUAL"]
9
- @outdated = data["OUTDATED"]
8
+ @actual = data['ACTUAL']
9
+ @outdated = data['OUTDATED']
10
10
  @actual ||= 0
11
11
  @outdated ||= 0
12
12
  end
13
-
14
13
  end
14
+ end
15
15
 
16
- end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module LMC
4
4
  class Device
5
- attr_reader :id, :name, :model, :serial, :heartbeatstate
5
+ attr_reader :id, :name, :model, :serial, :heartbeatstate, :cloud, :account, :status
6
6
 
7
7
  def initialize(data)
8
8
  @id = data['id']
@@ -17,51 +17,42 @@ module LMC
17
17
  @cloud ||= Cloud.instance
18
18
  end
19
19
 
20
- def get_config_for_account(account)
21
- response = @cloud.get ["cloud-service-config", "configbuilder", "accounts", account.id, "devices", @id, "ui"]
22
- JSON.parse(JSON.generate(response.body.to_h)) #terrible hack to get it to work for now. needs way to get more raw body_object from Response
23
- end
24
-
25
20
  def set_config_for_account(config, account)
26
- @cloud.put ["cloud-service-config", "configbuilder", "accounts", account.id, "devices", @id, "ui"], config
21
+ @cloud.put ['cloud-service-config', 'configbuilder', 'accounts', account.id, 'devices', @id, 'ui'], config
27
22
  end
28
23
 
29
24
  def get_monitor_widgets(widget_item_ids)
30
- @cloud.get ["cloud-service-monitoring", @account.id, "devices", @id, "monitordata"], { :widgetItemIds => widget_item_ids.join(",") }
25
+ @cloud.get ['cloud-service-monitoring', @account.id, 'devices', @id, 'monitordata'], :widgetItemIds => widget_item_ids.join(',')
31
26
  end
32
27
 
33
28
  def self.get_for_account(account)
34
29
  cloud = Cloud.instance
35
30
  cloud.auth_for_accounts [account.id]
36
- list = cloud.get ["cloud-service-devices", "accounts", account.id, "devices"]
31
+ list = cloud.get ['cloud-service-devices', 'accounts', account.id, 'devices']
37
32
  if list.code != 200
38
33
  puts "Error getting devices: #{list.body.message}"
39
34
  exit 1
40
35
  end
41
36
  devices = list.map do |data|
42
- data["account"] = account
37
+ data['account'] = account
43
38
  LMC::Device.new(data)
44
39
  end
45
- return devices
40
+ devices
46
41
  end
47
42
 
48
43
  def self.get_for_account_id(account_id)
49
- self.get_for_account Account.get(account_id)
44
+ get_for_account Account.get(account_id)
50
45
  end
51
46
 
52
47
  def config_state
53
48
  @config_state ||= get_config_state
54
49
  end
55
50
 
56
- def logs
57
- # https://lmctest/#/project/6392b234-b11c-498a-a077-a5f5b23c54a0/devices/compact/eaafa152-62cf-48a1-be65-b222886daa6d/logging
58
- #cloud = Cloud.instance
59
- ##cloud.auth_for_accounts [id]
60
- #cloud.get ["cloud-service-logging", "accounts", id, "logs?lang=DE"]
61
- raise "device logs not supported"
51
+ def config
52
+ LMC::DeviceConfig.new(@cloud, @account, self)
62
53
  end
63
54
 
64
- def record name
55
+ def record(name)
65
56
  MonitoringRecord.new(@cloud, @account, self, name)
66
57
  end
67
58
 
@@ -69,17 +60,21 @@ module LMC
69
60
  'DEVICE'
70
61
  end
71
62
 
63
+ def hwmask_hex
64
+ format format '%#010x', status['hwMask']
65
+ end
66
+
72
67
  private
73
68
 
69
+ ##
70
+ # TODO: This functionality is now duplicated in the DeviceConfig class and it's worse there.
74
71
  def get_config_state
75
- reply = @cloud.get ["cloud-service-config", "configdevice", "accounts", @account.id, "state"], { "deviceIds" => @id }
72
+ reply = @cloud.get ['cloud-service-config', 'configdevice', 'accounts', @account.id, 'state'], 'deviceIds' => @id
76
73
  if reply.code == 200
77
74
  # binding.pry
78
75
  DeviceConfigState.new reply.body[@id]
79
76
  end
80
77
  end
81
-
82
-
83
78
  end
84
-
85
79
  end
80
+
@@ -3,7 +3,6 @@
3
3
  require 'ostruct'
4
4
  module LMC
5
5
  class LMCResponse
6
-
7
6
  attr_reader :body, :code, :headers
8
7
 
9
8
  def initialize(response)
@@ -12,7 +11,7 @@ module LMC
12
11
  @body_object = JSON.parse response.body
13
12
  end
14
13
  if @body_object.class == Array
15
- @body = @body_object.map {|elem|
14
+ @body = @body_object.map { |elem|
16
15
  if elem.is_a? Hash then
17
16
  OpenStruct.new(elem)
18
17
  else
@@ -23,7 +22,7 @@ module LMC
23
22
  elsif @body_object.class == TrueClass || @body_object.class == FalseClass
24
23
  @body = @body_object
25
24
  else
26
- raise "Unknown json parse result"
25
+ raise "Unknown json parse result: #{@body_object.class}"
27
26
  end
28
27
  @code = response.code
29
28
  @headers = response.headers
@@ -59,5 +58,5 @@ module LMC
59
58
  @body_object.empty?
60
59
  end
61
60
  end
62
-
63
61
  end
62
+
@@ -3,38 +3,28 @@
3
3
  module LMC
4
4
  class Site
5
5
  attr_accessor :name
6
- attr_reader :id, :account
6
+ attr_reader :id, :account, :subnet_group_id
7
7
 
8
8
  def initialize(data, account)
9
- @cloud = Cloud.instance
10
- @id = data["id"]
11
- @name = data["name"]
12
- @subnet_group_id = data["subnetGroupId"]
9
+ @cloud = account.cloud
10
+ @cloud.auth_for_account account if account
13
11
  @account = account
12
+ data = @cloud.get ['cloud-service-devices', 'accounts', @account.id, 'sites', data] if data.is_a? UUID
13
+
14
+ @id = data['id']
15
+ @name = data['name']
16
+ @subnet_group_id = data['subnetGroupId']
14
17
 
15
- if @account
16
- @cloud.auth_for_account @account
17
- end
18
18
  end
19
19
 
20
20
  def to_s
21
21
  "#{@name}"
22
22
  end
23
23
 
24
- def account=(account)
25
- if @account == nil
26
- @account = account
27
- return true
28
- else
29
- raise "Cannot replace account for site"
30
- end
31
- end
32
-
33
24
  def configstates
34
- response = @cloud.get ["cloud-service-config", "configsubnetgroup", "accounts", @account.id, "subnetgroups", @subnet_group_id, "updatestates"]
35
- states = LMC::Configstates.new response.body
36
- return states
25
+ response = @cloud.get ['cloud-service-config', 'configsubnetgroup', 'accounts', @account.id, 'subnetgroups', @subnet_group_id, 'updatestates']
26
+ LMC::Configstates.new response.body
37
27
  end
38
28
  end
39
-
40
29
  end
30
+
@@ -2,28 +2,28 @@
2
2
 
3
3
  module LMC
4
4
  class User
5
- #todo: look into hiding password
5
+ # TODO: look into hiding password
6
6
  attr_reader :email
7
7
 
8
8
  def initialize(data)
9
- @email = data["email"]
10
- @password = data["password"]
9
+ @email = data['email']
10
+ @password = data['password']
11
11
  end
12
12
 
13
13
  # current registration process unclear and likely to have changed
14
14
  # since this code was written.
15
- #def register()
15
+ # def register()
16
16
  # # cloud instance by default authenticates.
17
17
  # # for registration specifically, no authentication is required
18
18
  # # should be possible without email and password for "admin" user
19
19
  # cloud = Cloud.instance
20
20
  # cloud.post ["cloud-service-auth", "users"], {"password" => @password, "email" => @email}
21
- #end
21
+ # end
22
22
 
23
23
  def update(old_pw)
24
24
  cloud = Cloud.instance
25
25
  begin
26
- cloud.post ["cloud-service-auth", "users", "self", "password"], {"password" => @password, 'verification' => old_pw}
26
+ cloud.post ['cloud-service-auth', 'users', 'self', 'password'], 'password' => @password, 'verification' => old_pw
27
27
  rescue RestClient::BadRequest => e
28
28
  response_body = JSON.parse(e.response)
29
29
  raise "#{e.message} - #{response_body['message']}"
@@ -32,11 +32,10 @@ module LMC
32
32
 
33
33
  def request_pw_reset
34
34
  action = AuthAction.new Cloud.instance
35
- action.type="PASSWORD_RESET"
36
- action.name=@email
35
+ action.type = 'PASSWORD_RESET'
36
+ action.name = @email
37
37
  action.post
38
38
  end
39
-
40
39
  end
40
+ end
41
41
 
42
- end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module LMC
4
4
  class AccountManager
5
- #@options
6
- #@global_options
7
- #@errors
5
+ # @options
6
+ # @global_options
7
+ # @errors
8
8
  attr_reader :errors
9
9
 
10
10
  def initialize(options, global_options)
@@ -15,59 +15,59 @@ module LMC
15
15
 
16
16
  # returns nil if invite did not work
17
17
  def invite(lmcen, distro, line, type, authority_name)
18
- lmcen.auth_for_accounts([distro["id"]])
18
+ lmcen.auth_for_accounts([distro['id']])
19
19
  line = line.strip.downcase
20
20
  begin
21
- account = Account.get(distro["id"])
21
+ account = Account.get(distro['id'])
22
22
  account_authorities = account.authorities
23
- #puts account_authorities.inspect if @global_options[:debug]
23
+ # puts account_authorities.inspect if @global_options[:debug]
24
24
  if @global_options[:debug]
25
25
  account_authorities.each do |a|
26
- puts a["name"]
26
+ puts a['name']
27
27
  puts a.inspect
28
28
  end
29
29
  end
30
- authority = account_authorities.find {|auth| auth["name"] == authority_name}
31
- #puts "account authorities: #{account_authorities}" if @global_options[:debug]
30
+ authority = account_authorities.find { |auth| auth['name'] == authority_name }
31
+ # puts "account authorities: #{account_authorities}" if @global_options[:debug]
32
32
  puts authority if @global_options[:debug]
33
33
  if !@options[:dry]
34
- # TODO wenn der default authority nicht gibt geht das ding kaputt
35
- invited = lmcen.invite_user_to_account(line, distro["id"], type, [authority["id"]])
36
- puts "Invite response:" + invited.inspect if @global_options[:debug]
37
- puts "Invited " + invited["name"].to_s + " to account #{distro["name"]}(#{invited.code})." if @global_options[:v]
34
+ # TODO: wenn der default authority nicht gibt geht das ding kaputt
35
+ invited = lmcen.invite_user_to_account(line, distro['id'], type, [authority['id']])
36
+ puts 'Invite response:' + invited.inspect if @global_options[:debug]
37
+ puts 'Invited ' + invited['name'].to_s + " to account #{distro['name']}(#{invited.code})." if @global_options[:v]
38
38
  if invited.code != 200
39
- @errors << {:line => line, :result => invited.body}
39
+ @errors << { :line => line, :result => invited.body }
40
40
  return nil
41
41
  end
42
42
  else
43
- invited = {"name" => line}
43
+ invited = { 'name' => line }
44
44
  end
45
45
 
46
- invite_url = lmcen.build_url("#", "register", Base64.encode64(invited["name"]))
47
- if @options["show-csv"]
48
- puts "\n" + invited["name"] + ", " + invite_url
46
+ invite_url = lmcen.build_url('#', 'register', Base64.encode64(invited['name']))
47
+ if @options['show-csv']
48
+ puts "\n" + invited['name'] + ', ' + invite_url
49
49
  end
50
- if @options["send-mail"] && invited
50
+ if @options['send-mail'] && invited
51
51
  mailbody = <<END
52
52
  Hallo,
53
- auf #{lmcen.cloud_host} wurde eine Einladung zu dem Account #{distro["name"]} für den Nutzer #{invited["name"]} erstellt.
53
+ auf #{lmcen.cloud_host} wurde eine Einladung zu dem Account #{distro['name']} für den Nutzer #{invited['name']} erstellt.
54
54
 
55
55
  Falls der Account noch nicht existiert, kann dieser Link zur Registrierung genutzt werden:
56
56
  END
57
57
  mail = Mail.new do
58
- from "Philipp Erbelding <philipp.erbelding@lancom.de>"
59
- to invited["name"]
60
- subject "Einladung auf " + lmcen.cloud_host
58
+ from 'Philipp Erbelding <philipp.erbelding@lancom.de>'
59
+ to invited['name']
60
+ subject 'Einladung auf ' + lmcen.cloud_host
61
61
  body mailbody + invite_url
62
62
  end
63
63
 
64
64
  puts invite_url
65
65
  if !@options[:dry]
66
- puts "sending mail to " + invited["name"]
67
- mail.delivery_method :smtp, address: "lcs-mail"
66
+ puts 'sending mail to ' + invited['name']
67
+ mail.delivery_method :smtp, address: 'lcs-mail'
68
68
  mail.deliver
69
69
  else
70
- puts "[DRY RUN] would send mail to " + invited["name"]
70
+ puts '[DRY RUN] would send mail to ' + invited['name']
71
71
  end
72
72
  puts mail.to_s if @global_options[:v]
73
73
  end
@@ -77,9 +77,10 @@ END
77
77
  puts invited.inspect
78
78
  resp = JSON.parse(e.response)
79
79
  puts resp.inspect
80
- puts resp["message"]
80
+ puts resp['message']
81
81
  end
82
- return true
82
+ true
83
83
  end
84
84
  end
85
85
  end
86
+
@@ -14,9 +14,10 @@ module LMC
14
14
  def service_name
15
15
  'auth'
16
16
  end
17
+
17
18
  def collection_name
18
19
  'actions'
19
20
  end
20
-
21
21
  end
22
- end
22
+ end
23
+
@@ -5,6 +5,7 @@ module LMC
5
5
  attr_accessor :account, :id, :name, :visibility, :type
6
6
 
7
7
  def initialize(data, account)
8
+ # TODO: Use cloud object from account
8
9
  @cloud = Cloud.instance
9
10
  apply_data(data)
10
11
  @account = account
@@ -14,26 +15,26 @@ module LMC
14
15
  # GET /accounts/{accountId}/authorities/{authorityId}/rights
15
16
  @cloud.auth_for_account @account
16
17
  response = @cloud.get ['cloud-service-auth', 'accounts', @account.id, 'authorities', @id, 'rights']
17
- return response.body.to_s
18
+ response.body.to_s
18
19
  end
19
20
 
20
- #returns itself, allows chaining
21
+ # returns itself, allows chaining
21
22
  def save
22
23
  response = if @id.nil?
23
- Cloud.instance.post ["cloud-service-auth", 'accounts', @account.id, "authorities"], self
24
+ Cloud.instance.post ['cloud-service-auth', 'accounts', @account.id, 'authorities'], self
24
25
  else
25
- raise "editing authorities not supported"
26
- #@cloud.put ["cloud-service-auth", "principals", @id], self
26
+ raise 'editing authorities not supported'
27
+ # @cloud.put ["cloud-service-auth", "principals", @id], self
27
28
  end
28
29
  apply_data(response.body)
29
- return self
30
+ self
30
31
  end
31
32
 
32
33
  def to_json(*a)
33
34
  {
34
- "name" => @name,
35
- "type" => @type,
36
- "visibility" => @visibility
35
+ 'name' => @name,
36
+ 'type' => @type,
37
+ 'visibility' => @visibility
37
38
  }.to_json(*a)
38
39
  end
39
40
 
@@ -51,3 +52,4 @@ module LMC
51
52
  end
52
53
  end
53
54
  end
55
+
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LMC
4
+ # Represents a device config in LMC
5
+ class DeviceConfig
6
+ attr_reader :state
7
+
8
+ def url_configbuilder
9
+ ['cloud-service-config', 'configbuilder', 'accounts',
10
+ @account.id, 'devices', @device.id, 'ui']
11
+ end
12
+
13
+ def url_ticket
14
+ ['cloud-service-config',
15
+ 'configbuilder',
16
+ 'accounts',
17
+ @account.id,
18
+ 'devices',
19
+ @device.id,
20
+ 'tickets',
21
+ @ticket_id]
22
+ end
23
+
24
+ def url_state
25
+ %W(cloud-service-config configdevice accounts #{@account.id} state)
26
+ end
27
+
28
+ # def url_stringtable
29
+ # ['cloud-service-config', 'configdsc', 'stringtable', dscui['stringtableId']]
30
+ # end
31
+
32
+ def initialize(cloud, account, device)
33
+ @cloud = cloud
34
+ @account = account
35
+ @device = device
36
+ @response = nil
37
+ @ticket_id = nil
38
+ # state returns an object with each requested device id pointing to a state object.
39
+ @state = @cloud.get(url_state, deviceIds: @device.id.to_s).body[@device.id]
40
+ end
41
+
42
+ def configjson
43
+ confighash.to_json
44
+ end
45
+
46
+ def confighash
47
+ items.to_h
48
+ end
49
+
50
+ ##
51
+ # Returns a hash similar to #confighash but with the OIDs replaced with more
52
+ # meaningful descriptions.
53
+
54
+ def descriptive_confighash
55
+ item_map = dscui.item_by_id_map
56
+ confighash.map { |k, v|
57
+ [item_map[k].description, v]
58
+ }.to_h
59
+ end
60
+
61
+ def items
62
+ response.items
63
+ end
64
+
65
+ def dscui
66
+ @dscui ||= DeviceDSCUi.new @device
67
+ end
68
+
69
+ def lcf
70
+ # lfc format findings:
71
+ # group headings in {} do not matter
72
+ # indentation does not matter
73
+ # table oids need to be enclosed in <>
74
+ # table cell oids need to be enclosed in () and should follow table oids
75
+
76
+ result = ''
77
+ result += lcf_header
78
+ items.each do |key, value|
79
+ if value.instance_of? String
80
+ result += "#{key} = #{value}\n"
81
+ elsif value.instance_of? Hash
82
+ rows = value['rows']
83
+ col_ids = value['colIds']
84
+ if rows.length > 0
85
+ result += "<#{key}>\n"
86
+ rows.each_with_index { |row, index|
87
+ row.each_with_index { |col, col_index|
88
+ result += "(#{key}.#{index + 1}.#{col_ids[col_index]}) = #{col}\n"}
89
+ }
90
+ end
91
+ else
92
+ raise 'Unexpected value in config items: ' + value.class.to_s
93
+ end
94
+ end
95
+ result += lcf_footer
96
+ end
97
+
98
+ def current_device_type
99
+ OpenStruct.new JSON.parse state['currentDeviceType']
100
+ end
101
+
102
+ ##
103
+ # @return [String]
104
+ def feature_mask_lower32_hex
105
+ feature_mask = 0
106
+ lower_features = current_device_type.features.select { |feature| feature < 32 }
107
+ lower_features.each do |feature_pos|
108
+ feature = 2**feature_pos
109
+ feature_mask = feature_mask | feature
110
+ end
111
+ if feature_mask == 0
112
+ '0x00000000'
113
+ else
114
+ format '%#010x', feature_mask
115
+ end
116
+ end
117
+
118
+ def lcf_device_version
119
+ v = 'v'
120
+ v += "#{@device.status['fwMajor']}."
121
+ v += "#{@device.status['fwMinor']}."
122
+ v + format('%04d', @device.status['fwBuild'])
123
+ end
124
+
125
+ ##
126
+ # Gives the feature IDs as a string like this: "IDs:2,3,f"
127
+ # @return [String]
128
+ def lcf_feature_id_string
129
+ hex_features = current_device_type.features.map { |feature| feature.to_s 16 }
130
+ "IDs:#{hex_features.join(',')}"
131
+ end
132
+
133
+ private
134
+
135
+ ##
136
+ # Produces lcf header
137
+ # @return [String]
138
+ def lcf_header
139
+ "(LMC Configuration of '#{@device.name}' at #{Time.now} via ruby-lmc #{LMC::VERSION})
140
+ (#{@device.status['fwLabel']}) (#{lcf_feature_hw_string})
141
+ [#{@device.model}] #{lcf_device_version}
142
+ [TYPE: LCF; VERSION: 1.00; HASHTYPE: none;]
143
+ "
144
+ end
145
+
146
+ ##
147
+ # Gives the feature mask, the feature IDs and the hardware mask as string for the lcf
148
+ # header like this: "0x0000c010,IDs:4,e,f//e0901447,2b;0x0c000002"
149
+ # @return [String]
150
+ def lcf_feature_hw_string
151
+ "#{feature_mask_lower32_hex},#{lcf_feature_id_string};#{@device.hwmask_hex}"
152
+ end
153
+
154
+ def lcf_footer
155
+ "[END: LCF;]\n"
156
+ end
157
+
158
+ def response
159
+ return @response unless @response.nil?
160
+ fetch_result
161
+ end
162
+
163
+ def fetch_result
164
+ response_or_ticket = @cloud.get(url_configbuilder).body
165
+ if response_or_ticket.respond_to? 'ticketId'
166
+ @ticket_id = response_or_ticket.ticketId
167
+ redeem_ticket 5
168
+ else
169
+ @response = response_or_ticket
170
+ end
171
+ @response
172
+ end
173
+
174
+ def redeem_ticket(tries)
175
+ attempts = 1
176
+ until @response
177
+ raise 'Too many attempts' if attempts > tries
178
+ attempts += 1
179
+ body = @cloud.get(url_ticket).body
180
+ unless body.respond_to? :ticketId
181
+ @ticket_id = nil
182
+ @response = body
183
+ end
184
+ sleep 0.5
185
+ end
186
+ end
187
+
188
+ # def stringtable
189
+ # @stringtable ||= @cloud.get(url_stringtable).body
190
+ # end
191
+ end
192
+ end
193
+