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 +4 -4
- data/README.md +17 -16
- data/app/helpers/concerns/foreman_teamdynamix/hosts_helper_extensions.rb +4 -10
- data/app/models/concerns/foreman_teamdynamix/host_extensions.rb +38 -17
- data/app/services/teamdynamix_api.rb +37 -33
- data/lib/foreman_teamdynamix/version.rb +1 -1
- data/lib/tasks/teamdynamix.rake +22 -28
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ce4c2a7a334ce842ffc0d301d3bb3698eec89e0
|
4
|
+
data.tar.gz: 1e757e66050495e84c00be995dc3a6db5ff78433
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
-
|
36
|
-
|
37
|
-
|
38
|
-
-
|
39
|
-
|
40
|
-
|
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
|
-
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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][
|
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 '
|
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.
|
14
|
+
return [[_('Asset'), 'None Associated or error from Team Dynamix']] unless @host.teamdynamix_asset
|
15
15
|
|
16
|
-
|
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.
|
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[
|
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
|
-
|
10
|
-
|
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
|
-
|
19
|
+
def teamdynamix_asset(search = false)
|
20
|
+
@teamdynamix_asset ||= td_api.get_asset(teamdynamix_asset_uid)
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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(
|
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
|
-
|
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(
|
44
|
-
uri = URI.parse("#{API_URL}/#{APP_ID}/assets/#{
|
45
|
-
rest(:post, uri, retire_asset_payload(
|
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
|
117
|
-
SerialNumber
|
118
|
-
Name
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
[
|
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
|
data/lib/tasks/teamdynamix.rake
CHANGED
@@ -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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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: #{
|
46
|
-
unless
|
47
|
-
|
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
|
+
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-
|
11
|
+
date: 2018-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deface
|