foreman_teamdynamix 0.4.2 → 0.5.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: 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