在ansbile的setup上的一些扩展功能

说明

为了配合做的资产信息搜集, 直接在setup模块上加上了raid卡,底层的物理磁盘,内存等相关信息.

一些改变

  • 取消了原本关于磁盘单位的自动格式,改为磁盘的最大单位支持到GB,如果需要和原生一下,那在代码文件的 314-317 行注释

依赖说明

底层的raid和磁盘信息收集需要安装MegaCli64包,关于安装方法,自行解决.
另外关于raid级别,怎么推算成raid5或者raid1+0的. 因为我线上只有这2种,所以如果需要的话,自行解决raid级别的判断.

代码文件

把下面的代码保存为扩展模块,比如文件名setupex.py, 放在你ansible.cfg配置文件中library定义的路径下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: setup
version_added: historical
short_description: Gathers facts about remote hosts
options:
gather_subset:
version_added: "2.1"
description:
- "if supplied, restrict the additional facts collected to the given subset.
Possible values: all, hardware, network, virtual, ohai, and
facter Can specify a list of values to specify a larger subset.
Values can also be used with an initial C(!) to specify that
that specific subset should not be collected. For instance:
!hardware, !network, !virtual, !ohai, !facter. Note that a few
facts are always collected. Use the filter parameter if you do
not want to display those."
required: false
default: 'all'
filter:
version_added: "1.1"
description:
- if supplied, only return facts that match this shell-style (fnmatch) wildcard.
required: false
default: '*'
fact_path:
version_added: "1.3"
description:
- path used for local ansible facts (*.fact) - files in this dir
will be run (if executable) and their results be added to ansible_local facts
if a file is not executable it is read. Check notes for Windows options. (from 2.1 on)
File/results format can be json or ini-format
required: false
default: '/etc/ansible/facts.d'
description:
- This module is automatically called by playbooks to gather useful
variables about remote hosts that can be used in playbooks. It can also be
executed directly by C(/usr/bin/ansible) to check what variables are
available to a host. Ansible provides many I(facts) about the system,
automatically.
notes:
- More ansible facts will be added with successive releases. If I(facter) or
I(ohai) are installed, variables from these programs will also be snapshotted
into the JSON file for usage in templating. These variables are prefixed
with C(facter_) and C(ohai_) so it's easy to tell their source. All variables are
bubbled up to the caller. Using the ansible facts and choosing to not
install I(facter) and I(ohai) means you can avoid Ruby-dependencies on your
remote systems. (See also M(facter) and M(ohai).)
- The filter option filters only the first level subkey below ansible_facts.
- If the target host is Windows, you will not currently have the ability to use
C(filter) as this is provided by a simpler implementation of the module.
- If the target host is Windows you can now use C(fact_path). Make sure that this path
exists on the target host. Files in this path MUST be PowerShell scripts (``*.ps1``) and
their output must be formattable in JSON (Ansible will take care of this). Test the
output of your scripts.
This option was added in Ansible 2.1.
author:
- "Ansible Core Team"
- "Michael DeHaan"
- "David O'Brien @david_obrien davidobrien1985"
'''
EXAMPLES = """
# Display facts from all hosts and store them indexed by I(hostname) at C(/tmp/facts).
ansible all -m setup --tree /tmp/facts
# Display only facts regarding memory found by ansible on all hosts and output them.
ansible all -m setup -a 'filter=ansible_*_mb'
# Display only facts returned by facter.
ansible all -m setup -a 'filter=facter_*'
# Display only facts about certain interfaces.
ansible all -m setup -a 'filter=ansible_eth[0-2]'
# Restrict additional gathered facts to network and virtual.
ansible all -m setup -a 'gather_subset=network,virtual'
# Do not call puppet facter or ohai even if present.
ansible all -m setup -a 'gather_subset=!facter,!ohai'
# Only collect the minimum amount of facts:
ansible all -m setup -a 'gather_subset=!all'
# Display facts from Windows hosts with custom facts stored in C(C:\\custom_facts).
ansible windows -m setup -a "fact_path='c:\\custom_facts'"
"""
class DogoFacts(object):
def __init__(self, is_virtual_type=False):
self.facts = dict()
self.level = {
'RAID-5': ['Primary-5, Secondary-0, RAID Level Qualifier-3', '3', '1'],
'RAID-1': ['Primary-1, Secondary-0, RAID Level Qualifier-0', '2', '1'],
'RAID-10': ['Primary-1, Secondary-0, RAID Level Qualifier-0', '2', '2'],
'RAID-0': ['Primary-0, Secondary-0, RAID Level Qualifier-0', '2', '1']
}
self.facts['dogo_raid_level'] = 'NA'
self.facts['dogo_disk_size'] = 'NA'
self.facts['dogo_raid_disk'] = []
self.facts['dogo_memory_detail'] = []
self.facts['dogo_raid_info'] = []
if is_virtual_type is False:
self.get_dogo_facts()
def populate(self):
cwd = os.getcwd()
try:
return self.facts
finally:
clear_files = [os.path.join(cwd, 'MegaSAS.log'), os.path.join(cwd, 'CmdTool.log')]
for f in clear_files:
if os.path.isfile(f): os.remove(f)
def get_dogo_facts(self):
""" 阵列级别
:return:
"""
MegaCli64_bin = '/opt/MegaRAID/MegaCli/MegaCli64'
dmidecode_bin = '/usr/sbin/dmidecode'
cmd_info = []
if os.path.exists(MegaCli64_bin):
cmd_info.extend([
dict(
cmd="%s -cfgdsply -aALL|egrep -i '^(Adapter:|Product Name:|Memory:|Serial No:)'" % MegaCli64_bin,
cb=self._raid_info_format
),
dict(
cmd="%s -LDInfo -Lall -aALL|egrep -i '^(RAID Level|Size|Number Of Drives|Span Depth)'" % MegaCli64_bin,
cb=self._raid_level_format
),
dict(
cmd="%s -PDList -aAll|egrep -i '^(Slot Number|PD Type|Raw Size|Device Firmware Level|Inquiry Data):'" % MegaCli64_bin,
cb=self._raid_disk_format
)])
if os.path.exists(dmidecode_bin):
cmd_info.append(dict(
cmd='%s -t memory' % dmidecode_bin,
cb=self._get_memory_format
))
for c in cmd_info:
cmd = c['cmd']
(rc, out, err) = module.run_command(cmd, use_unsafe_shell=True)
if rc == 0:
c['cb'](out)
def _raid_info_format(self, data):
""" 阵列卡信息
Product Name: PERC H710 Mini
Memory: 512MB
Serial No: 3C801FE
:param data:
:return:
"""
key = ['adapter', 'name', 'memory', 'serial']
v = []
for line in data.splitlines():
v.append(line.split(': ')[-1].strip())
self.facts['dogo_raid_info'].append(dict(zip(key, v)))
def _raid_level_format(self, data):
""" 阵列卡级别
RAID Level : Primary-1, Secondary-0, RAID Level Qualifier-0
Size : 557.75 GB
Number Of Drives per span:2
Span Depth : 2
:param data:
:return:
"""
out_lines = data.splitlines()
size_line = out_lines[1]
level_line = [out_lines[0]] + out_lines[2:]
info = [line.split(':')[-1].strip() for line in level_line if len(line.split(':')) == 2]
if len(info) == 3:
st = [k for (k, v) in self.level.items() if set(info) == set(v)]
if st:
self.facts['dogo_raid_level'] = st[0]
self.facts['dogo_disk_size'] = module.to_GB(size_line.split(': ')[-1].strip())
def _raid_disk_format(self, data):
""" 阵列底层磁盘
Slot Number: 0
PD Type: SAS
Raw Size: 279.396 GB [0x22ecb25c Sectors]
Device Firmware Level: ES66
Inquiry Data: SEAGATE ST3300657SS ES666SJ7CM0P
~~~~
Slot Number: 1
PD Type: SAS
Raw Size: 279.396 GB [0x22ecb25c Sectors]
Device Firmware Level: ES66
Inquiry Data: SEAGATE ST3300657SS ES666SJ7CNZV
~~~~
Slot Number: 2
PD Type: SAS
Raw Size: 279.396 GB [0x22ecb25c Sectors]
Device Firmware Level: ES66
Inquiry Data: SEAGATE ST3300657SS ES666SJ7RZYA
~~~~
Slot Number: 3
PD Type: SAS
Raw Size: 279.396 GB [0x22ecb25c Sectors]
Device Firmware Level: ES66
Inquiry Data: SEAGATE ST3300657SS ES666SJ6L2A8
:param result:
:return:
"""
rt = []
i = 1
v = {}
for line in data.splitlines():
if i % 5 == 1:
v['slot_number'] = line.split(': ')[-1]
elif i % 5 == 2:
v['type'] = line.split(': ')[-1]
elif i % 5 == 3:
v['size'] = ' '.join(line.split(' ')[2:4])
elif i % 5 == 4:
v['fireware_level'] = line.split(': ')[-1]
else:
v['name'] = re.sub(r'[ ]+', ' ', line.split(':')[-1]).strip()
rt.append(v)
v = {}
i += 1
self.facts['dogo_raid_disk'] = rt
def _get_memory_format(self, data):
""" 内存详细
:return:
"""
rt = []
key = ['size', 'locator', 'type', 'speed', 'serial_number', 'part_number']
RE_MEMORY = re.compile(ur'Memory Device[\s\S]*?'
ur'Size:\s+(?P<Size>\d+.*$)[\s\S]*?'
ur'Locator:\s+(?P<Locator>\w+)[\s\S]*?'
ur'Type:\s+(?P<Type>\w+)[\s\S]*?'
ur'Speed:\s+(?P<Speed>.*$)[\s\S]*?'
ur'Serial Number:\s+(?P<serial_number>\w+)[\s\S]*?'
ur'Part Number:\s+(?P<part_number>\S+)',
re.M
)
for line in RE_MEMORY.findall(data):
rt.append(dict(zip(key, line)))
self.facts['dogo_memory_detail'] = rt
def main():
global module
module = AnsibleModuleBase(
argument_spec=dict(
gather_subset=dict(default=["all"], required=False, type='list'),
filter=dict(default="*", required=False),
fact_path=dict(default='/etc/ansible/facts.d', required=False),
),
supports_check_mode=True,
)
data = get_all_facts(module)
is_virtual_type = False if data['ansible_facts'].get('ansible_virtualization_type', 'NA') == 'NA' else True
ex_facts = DogoFacts(is_virtual_type=is_virtual_type).populate()
data['ansible_facts'].update(**ex_facts)
module.exit_json(**data)
# import module snippets
from ansible.module_utils.basic import *
from ansible.module_utils.facts import *
class AnsibleModuleBase(AnsibleModule):
def __init__(self, *args, **kwargs):
super(AnsibleModuleBase, self).__init__(*args, **kwargs)
def pretty_bytes(self, size):
# maxsize to GB
ranges = (
# (1 << 70, 'ZB'),
# (1 << 60, 'EB'),
# (1 << 50, 'PB'),
# (1 << 40, 'TB'),
(1 << 30, 'GB'),
(1 << 20, 'MB'),
(1 << 10, 'KB'),
(1, 'Bytes')
)
for limit, suffix in ranges:
if size >= limit:
break
return '%.2f %s' % (float(size) / limit, suffix)
def to_GB(self, data):
# data : '1.58 TB'
ranges = dict(
TB=1 << 10,
PB=1 << 20,
EB=1 << 30,
ZB=1 << 40
)
lines = data.split(' ')
rt = data
if lines[-1] in ranges:
rt = '%.2f GB' % (float(lines[0]) * ranges[lines[1]],)
return rt
if __name__ == '__main__':
main()

测试

测试方法

1
# ansible localhost -m setupex

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{
"ansible_facts": {
...,
"dogo_disk_size": "557.75 GB",
"dogo_memory_detail": [
{
"locator": "DIMM_A1",
"part_number": "KP9RN2-HYC",
"serial_number": "2A2E4CB5",
"size": "8192 MB",
"speed": "1333 MHz",
"type": "DDR3"
},
{
"locator": "DIMM_A2",
"part_number": "9965516-100.A00LF",
"serial_number": "363AAE40",
"size": "8192 MB",
"speed": "1600 MHz",
"type": "DDR3"
},
{
"locator": "DIMM_B1",
"part_number": "KP9RN2-HYC",
"serial_number": "38363A83",
"size": "8192 MB",
"speed": "1333 MHz",
"type": "DDR3"
},
{
"locator": "DIMM_B2",
"part_number": "HMT31GR7AFR4A-H9",
"serial_number": "00000000",
"size": "8192 MB",
"speed": "1333 MHz",
"type": "DDR3"
}
],
"dogo_raid_disk": [
{
"fireware_level": "ES66",
"name": "SEAGATE ST3300657SS ES666SJ7CM0P",
"size": "279.396 GB",
"slot_number": "0",
"type": "SAS"
},
{
"fireware_level": "ES66",
"name": "SEAGATE ST3300657SS ES666SJ7CNZV",
"size": "279.396 GB",
"slot_number": "1",
"type": "SAS"
},
{
"fireware_level": "ES66",
"name": "SEAGATE ST3300657SS ES666SJ7RZYA",
"size": "279.396 GB",
"slot_number": "2",
"type": "SAS"
},
{
"fireware_level": "ES66",
"name": "SEAGATE ST3300657SS ES666SJ6L2A8",
"size": "279.396 GB",
"slot_number": "3",
"type": "SAS"
}
],
"dogo_raid_info": [
{
"adapter": "0",
"memory": "512MB",
"name": "PERC H710 Mini",
"serial": "3C801FE"
}
],
"dogo_raid_level": "RAID-10",
"module_setup": true
},
"changed": false
}
分享即快乐,谢谢你请思哲小朋友吃糖!