foreman_teamdynamix 0.4.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb9f0ae3f79be59738b81b9733f5ac3c8f5276b5
4
- data.tar.gz: 7ec2eb8f477890380ceb45957d91686d81618300
3
+ metadata.gz: 7ce4c2a7a334ce842ffc0d301d3bb3698eec89e0
4
+ data.tar.gz: 1e757e66050495e84c00be995dc3a6db5ff78433
5
5
  SHA512:
6
- metadata.gz: 06ea04572c6513ab8243c485cec4646e6261686d82fd5bc00989c8958a42111dae2822fc6014bfdc71beac1f93717a12551576c68191767009caa2a9a71cd26c
7
- data.tar.gz: 00e259b39c62e48baa6a828886992e5cebc30a1c1103102dc46ceb6b03324827f12bd95c7443f426c61481ef55f2eb0cefe87315348d97e71cb0e7e3c38f396d
6
+ metadata.gz: afc16bd54e8964593702e2dec8f412b1ccf3b031817d77a3ecc0cbb542fdeb05e97656719ab512b6d46d572c493f5e2741e26b0341e6fa24a89ac9fe9ece3b14
7
+ data.tar.gz: 83e332682132dd98bb7babae29789cccdd10a1144910992c653b64bb768c44e57719a7db8cce2db2b6a3b1ded201e94a6f34f05d6dd03af959e1ba28e3335037
data/README.md CHANGED
@@ -28,23 +28,23 @@ Example Configuration
28
28
  :username: 'xxxxxx'
29
29
  :password: 'xxxxxx'
30
30
  :create:
31
- :StatusID: 641
32
- :AcquisitionDate: host.created_at
33
- :OwningCustomerName: "'foreman_teamdynamix_plugin_test'"
34
- :Attributes:
35
- - name: mu.ci.Lifecycle Status
36
- id: 11634
37
- value: 26190
38
- - name: mu.ci.Description
39
- id: 11632
40
- value: >-
31
+ StatusID: 641
32
+ AcquisitionDate: host.created_at
33
+ OwningCustomerName: "'foreman_teamdynamix_plugin_test'"
34
+ Attributes:
35
+ - Name: mu.ci.Lifecycle Status
36
+ ID: 11634
37
+ Value: 26190
38
+ - Name: mu.ci.Description
39
+ ID: 11632
40
+ Value: >-
41
41
  "created by ForemanTeamdynamix plugin, owner is #{host.owner_id}"
42
- - name: Ticket Routing Details
43
- id: 11636
44
- value: >-
42
+ - Name: Ticket Routing Details
43
+ ID: 11636
44
+ Value: >-
45
45
  "Asset for host running on OS #{host.operatingsystem_id}"
46
46
  :delete:
47
- :StatusId: 642
47
+ StatusId: 642
48
48
  :fields:
49
49
  :url: https://miamioh.teamdynamix.com/SBTDNext/Apps
50
50
  Asset ID: ID
@@ -58,9 +58,10 @@ Example Configuration
58
58
  * All attributes are passed to the TeamDynamix API as is, while creating or deleting a TeamDynamix Asset.
59
59
  * An asset gets created or deleted with the Foreman Host create or delete life cycle event.
60
60
 
61
- [:api][:create][:Attributes]
61
+ [:api][:create][Attributes]
62
62
  * To configure any [Custom Attributes](https://api.teamdynamix.com/TDWebApi/Home/type/TeamDynamix.Api.CustomAttributes.CustomAttribute) for the asset.
63
- * It must contain expected value for 'id' and 'value' fields.
63
+ * It must contain expected value for 'Name', 'ID' and 'Value' fields.
64
+ * Notice the case of 'Name', 'ID' and 'Value', this must match for correct merging.
64
65
  * rest of the fields are optional, check the Custom Attribute's definition for what other fields are updatable.
65
66
  * Code evaluation is supported for custom attribute's value.
66
67
 
@@ -11,9 +11,9 @@ module ForemanTeamdynamix
11
11
 
12
12
  def teamdynamix_fields
13
13
  td_pane_fields = SETTINGS[:teamdynamix][:fields] || DEFAULT_TD_PANE_FIELDS
14
- return [[_('Asset'), 'None Associated']] unless @host.teamdynamix_asset_uid
14
+ return [[_('Asset'), 'None Associated or error from Team Dynamix']] unless @host.teamdynamix_asset
15
15
 
16
- get_teamdynamix_asset(@host.teamdynamix_asset_uid)
16
+ @asset = @host.teamdynamix_asset
17
17
 
18
18
  # display a link to the asset if url set
19
19
  fields = asset_uri(td_pane_fields)
@@ -38,12 +38,6 @@ module ForemanTeamdynamix
38
38
  end
39
39
  end
40
40
 
41
- def get_teamdynamix_asset(asset_id)
42
- @asset = @host.td_api.get_asset(asset_id)
43
- rescue StandardError => e
44
- raise "Error getting asset Data from Team Dynamix: #{e.message}"
45
- end
46
-
47
41
  def get_nested_attrib_val(nested_attrib)
48
42
  nested_attrib_tokens = nested_attrib.split('.')
49
43
  parent_attrib = nested_attrib_tokens.first
@@ -52,9 +46,9 @@ module ForemanTeamdynamix
52
46
  child_attrib.delete!("'")
53
47
  parent_attrib_val = @asset[parent_attrib]
54
48
  return '' if parent_attrib_val.blank?
55
- nested_attrib_val = parent_attrib_val.select { |attrib| attrib['Name'] == child_attrib }
49
+ nested_attrib_val = parent_attrib_val.find { |attrib| attrib['Name'] == child_attrib }
56
50
  return '' if nested_attrib_val.blank?
57
- nested_attrib_val[0]['Value']
51
+ nested_attrib_val['Value']
58
52
  end
59
53
  end
60
54
  end
@@ -2,38 +2,59 @@ module ForemanTeamdynamix
2
2
  module HostExtensions
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ included do
6
+ before_create :create_or_update_teamdynamix_asset
7
+ before_destroy :retire_teamdynamix_asset
8
+ validates :teamdynamix_asset_uid, uniqueness: { :allow_blank => true }
9
+ end
10
+
5
11
  def td_api
6
12
  @td_api ||= TeamdynamixApi.instance
7
13
  end
8
14
 
9
- included do
10
- before_create :create_teamdynamix_asset
11
- before_destroy :retire_teamdynamix_asset
12
- validates :teamdynamix_asset_uid, uniqueness: { :allow_blank => true }
15
+ def teamdynamix_asset_status
16
+ @teamdynamix_asset_status
13
17
  end
14
18
 
15
- private
19
+ def teamdynamix_asset(search = false)
20
+ @teamdynamix_asset ||= td_api.get_asset(teamdynamix_asset_uid)
16
21
 
17
- def create_teamdynamix_asset
18
- # when the asset is already in teamdynamix
19
- assets = td_api.search_asset(SerialLike: name)
22
+ if search && !@teamdynamix_asset
23
+ assets = td_api.search_asset(SerialLike: name)
24
+ if assets.length == 1
25
+ @teamdynamix_asset = td_api.get_asset(assets.first['ID'])
26
+ self.teamdynamix_asset_uid = teamdynamix_asset['ID']
27
+ @teamdynamix_asset_status = :updated_search
28
+ elsif assets.length > 1
29
+ errors.add(:base, _("Search for asset in TeamDynamix failed: Found #{assets.length} matching assets"))
30
+ end
31
+ end
20
32
 
21
- if assets.empty?
22
- asset = td_api.create_asset(self)
23
- self.teamdynamix_asset_uid = asset['ID']
24
- elsif assets.length > 1
25
- raise 'Found more than 1 existing asset'
26
- else
27
- self.teamdynamix_asset_uid = assets.first['ID']
33
+ @teamdynamix_asset
34
+ end
35
+
36
+ def create_or_update_teamdynamix_asset(save = false)
37
+ if teamdynamix_asset(true)
28
38
  td_api.update_asset(self)
39
+ @teamdynamix_asset_status ||= :updated_id
40
+ self.save if save
41
+ elsif errors.empty?
42
+ @teamdynamix_asset = td_api.create_asset(self)
43
+ self.teamdynamix_asset_uid = teamdynamix_asset['ID']
44
+ @teamdynamix_asset_status = :created
45
+ self.save if save
46
+ else
47
+ false
29
48
  end
30
49
  rescue StandardError => e
31
- errors.add(:base, _("Could not create the asset for the host in TeamDynamix: #{e.message}"))
50
+ errors.add(:base, _("Could not create or update the asset for the host in TeamDynamix: #{e.message}"))
32
51
  false
33
52
  end
34
53
 
54
+ private
55
+
35
56
  def retire_teamdynamix_asset
36
- td_api.retire_asset(teamdynamix_asset_uid) if teamdynamix_asset_uid
57
+ td_api.retire_asset(self) if teamdynamix_asset
37
58
  rescue StandardError => e
38
59
  errors.add(:base, _("Could not retire the asset for the host in TeamDynamix: #{e.message}"))
39
60
  false
@@ -20,14 +20,11 @@ class TeamdynamixApi
20
20
 
21
21
  # returns TeamDynamix.Api.Assets.Asset
22
22
  def get_asset(asset_id)
23
+ return nil unless asset_id
23
24
  uri = URI.parse("#{API_URL}/#{APP_ID}/assets/#{asset_id}")
24
25
  rest(:get, uri)
25
- end
26
-
27
- def asset_exist?(asset_id)
28
- get_asset(asset_id).present?
29
26
  rescue RuntimeError
30
- false
27
+ nil
31
28
  end
32
29
 
33
30
  def create_asset(host)
@@ -40,9 +37,9 @@ class TeamdynamixApi
40
37
  rest(:post, uri, update_asset_payload(host))
41
38
  end
42
39
 
43
- def retire_asset(asset_id)
44
- uri = URI.parse("#{API_URL}/#{APP_ID}/assets/#{asset_id}")
45
- rest(:post, uri, retire_asset_payload(asset_id))
40
+ def retire_asset(host)
41
+ uri = URI.parse("#{API_URL}/#{APP_ID}/assets/#{host.teamdynamix_asset_uid}")
42
+ rest(:post, uri, retire_asset_payload(host))
46
43
  end
47
44
 
48
45
  # Gets a list of assets matching the specified criteria. (IEnumerable(Of TeamDynamix.Api.Assets.Asset))
@@ -106,26 +103,38 @@ class TeamdynamixApi
106
103
  end
107
104
  end
108
105
 
109
- def retire_asset_payload(asset_id)
110
- asset = get_asset(asset_id)
111
- asset.merge(API_CONFIG[:delete].stringify_keys)
112
- end
113
-
114
106
  def create_asset_payload(host)
115
107
  ensure_configured_create_params
116
- default_attrs = { AppID: APP_ID,
117
- SerialNumber: host.name,
118
- Name: host.fqdn }
119
- create_attrs = evaluate_attributes(API_CONFIG[:create], host)
120
- default_attrs.merge(create_attrs)
121
- end
122
-
123
- def evaluate_attributes(create_attrs, host)
124
- create_attrs.symbolize_keys.each_with_object({}) do |(k, v), h|
125
- if k.eql?(:Attributes)
126
- h[:Attributes] = v.each_with_object([]) do |attribute, a|
127
- a << attribute.transform_keys(&:downcase)
128
- a.last['value'] = eval_attribute(a.last['value'], host)
108
+ default_attrs = { 'AppID' => APP_ID,
109
+ 'SerialNumber' => host.name,
110
+ 'Name' => host.fqdn }
111
+ evaluate_attributes(API_CONFIG[:create], host, default_attrs)
112
+ end
113
+
114
+ def update_asset_payload(host)
115
+ default_attrs = { 'AppID' => APP_ID,
116
+ 'SerialNumber' => host.name,
117
+ 'Name' => host.fqdn }
118
+ evaluate_attributes(API_CONFIG[:create], host, host.teamdynamix_asset.merge(default_attrs))
119
+ end
120
+
121
+ def retire_asset_payload(host)
122
+ evaluate_attributes(API_CONFIG[:delete], host, host.teamdynamix_asset)
123
+ end
124
+
125
+ def evaluate_attributes(attrs, host, asset = {})
126
+ attrs.stringify_keys.each_with_object(asset) do |(k, v), h|
127
+ if k.eql?('Attributes')
128
+ h['Attributes'] ||= []
129
+ v.each do |attrib|
130
+ attrib_c = attrib.stringify_keys
131
+ match = h['Attributes'].find { |attrib_a| attrib_a['Name'] == attrib_c['Name'] }
132
+ if match
133
+ match['Value'] = eval_attribute(attrib_c['Value'], host)
134
+ else
135
+ attrib_c['Value'] = eval_attribute(attrib_c['Value'], host)
136
+ h['Attributes'] << attrib_c
137
+ end
129
138
  end
130
139
  else
131
140
  h[k] = eval_attribute(v, host)
@@ -140,21 +149,16 @@ class TeamdynamixApi
140
149
  end
141
150
 
142
151
  def must_configure_create_params
143
- [:StatusID]
152
+ ['StatusID']
144
153
  end
145
154
 
146
155
  def valid_auth_token?(token)
147
156
  token.match(/^[a-zA-Z0-9\.\-\_]*$/)
148
157
  end
149
158
 
150
- def update_asset_payload(host)
151
- asset = get_asset(host.teamdynamix_asset_uid)
152
- asset.merge(create_asset_payload(host))
153
- end
154
-
155
159
  def ensure_configured_create_params
156
160
  must_configure_create_params.each do |must_configure_param|
157
- unless API_CONFIG[:create].include?(must_configure_param)
161
+ unless API_CONFIG[:create].include?(must_configure_param) || API_CONFIG[:create].include?(must_configure_param.to_sym)
158
162
  raise("#{must_configure_param} is required. Set it as a configuration item.")
159
163
  end
160
164
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTeamdynamix
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
@@ -6,47 +6,41 @@ desc <<-DESC.strip_heredoc.squish
6
6
  It could be run for all the hosts as:
7
7
  * rake teamydynamix:sync:hosts
8
8
 
9
+ Available options:
10
+ * where => where string for limiting host query
11
+ * limit => limit for limiting host query
12
+
9
13
  DESC
10
14
  namespace :teamdynamix do
11
15
  namespace :sync do
12
16
  task :hosts => :environment do
13
- td_api = TeamdynamixApi.instance
14
17
  errors = []
15
- creates = 0
16
- updates_from_serial_matching = 0
17
- update_from_asset_id = 0
18
+ created = 0
19
+ updated_search = 0
20
+ updated_id = 0
18
21
 
19
22
  console_user = User.find_by(login: 'foreman_console_admin')
20
23
  User.current = console_user
21
24
 
22
- Host.all.each do |h|
23
- # if asset exists, update it
24
- if td_api.asset_exist?(h.teamdynamix_asset_uid)
25
- td_api.update_asset(h)
26
- update_from_asset_id += 1
27
- else
28
- assets = td_api.search_asset(SerialLike: h.name)
29
- if assets.empty?
30
- asset = td_api.create_asset(h)
31
- h.teamdynamix_asset_uid = asset['ID']
32
- errors.push("Could not save host: #{h.name} (#{h.id})") unless h.save
33
- creates += 1
34
- elsif assets.length > 1
35
- errors.push("Could not sync: Found more than 1 asset for #{h.name} (#{h.id})")
36
- else
37
- h.teamdynamix_asset_uid = assets.first['ID']
38
- td_api.update_asset(h)
39
- errors.push("Could not save host: #{h.name} (#{h.id})") unless h.save
40
- updates_from_serial_matching += 1
25
+ hosts = Host
26
+ hosts = hosts.where(ENV['where']) if ENV['where']
27
+ hosts = hosts.limit(ENV['limit']) if ENV['limit']
28
+
29
+ hosts.all.each do |h|
30
+ if h.create_or_update_teamdynamix_asset(true)
31
+ case h.teamdynamix_asset_status
32
+ when :created then created += 1
33
+ when :updated_search then updated_search += 1
34
+ when :updated_id then updated_id += 1
41
35
  end
36
+ else
37
+ errors.push("Could not save host: #{h.name} (#{h.id}):\n #{h.errors.full_messages.join("\n ")}")
42
38
  end
43
39
  sleep(1.5) # TD only allows 60 api calls per minute
44
40
  end
45
- puts "Assets created: #{creates}" unless creates.eql?(0)
46
- unless updates_from_serial_matching.eql?(0)
47
- puts "Assets updated from serial search: #{updates_from_serial_matching}"
48
- end
49
- puts "Assets updated from ID: #{update_from_asset_id}" unless update_from_asset_id.eql?(0)
41
+ puts "Assets created: #{created}" unless created.eql?(0)
42
+ puts "Assets updated from serial search: #{updated_search}" unless updated_search.eql?(0)
43
+ puts "Assets updated from ID: #{updated_id}" unless updated_id.eql?(0)
50
44
  puts "Errors:\n#{errors.join("\n")}" unless errors.empty?
51
45
  end
52
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_teamdynamix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nipendar Tyagi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-03 00:00:00.000000000 Z
11
+ date: 2018-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface