yedit.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. #!/usr/bin/env python
  2. # pylint: disable=missing-docstring
  3. # ___ ___ _ _ ___ ___ _ _____ ___ ___
  4. # / __| __| \| | __| _ \ /_\_ _| __| \
  5. # | (_ | _|| .` | _|| / / _ \| | | _|| |) |
  6. # \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
  7. # | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
  8. # | |) | (_) | | .` | (_) || | | _|| |) | | | |
  9. # |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
  10. #
  11. # Copyright 2016 Red Hat, Inc. and/or its affiliates
  12. # and other contributors as indicated by the @author tags.
  13. #
  14. # Licensed under the Apache License, Version 2.0 (the "License");
  15. # you may not use this file except in compliance with the License.
  16. # You may obtain a copy of the License at
  17. #
  18. # http://www.apache.org/licenses/LICENSE-2.0
  19. #
  20. # Unless required by applicable law or agreed to in writing, software
  21. # distributed under the License is distributed on an "AS IS" BASIS,
  22. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. # See the License for the specific language governing permissions and
  24. # limitations under the License.
  25. #
  26. # -*- -*- -*- Begin included fragment: class/import.py -*- -*- -*-
  27. # pylint: disable=wrong-import-order
  28. import json
  29. import os
  30. import re
  31. # pylint: disable=import-error
  32. import ruamel.yaml as yaml
  33. import shutil
  34. from ansible.module_utils.basic import AnsibleModule
  35. # -*- -*- -*- End included fragment: class/import.py -*- -*- -*-
  36. # -*- -*- -*- Begin included fragment: doc/yedit -*- -*- -*-
  37. DOCUMENTATION = '''
  38. ---
  39. module: yedit
  40. short_description: Create, modify, and idempotently manage yaml files.
  41. description:
  42. - Modify yaml files programmatically.
  43. options:
  44. state:
  45. description:
  46. - State represents whether to create, modify, delete, or list yaml
  47. required: true
  48. default: present
  49. choices: ["present", "absent", "list"]
  50. aliases: []
  51. debug:
  52. description:
  53. - Turn on debug information.
  54. required: false
  55. default: false
  56. aliases: []
  57. src:
  58. description:
  59. - The file that is the target of the modifications.
  60. required: false
  61. default: None
  62. aliases: []
  63. content:
  64. description:
  65. - Content represents the yaml content you desire to work with. This
  66. - could be the file contents to write or the inmemory data to modify.
  67. required: false
  68. default: None
  69. aliases: []
  70. content_type:
  71. description:
  72. - The python type of the content parameter.
  73. required: false
  74. default: 'dict'
  75. aliases: []
  76. key:
  77. description:
  78. - The path to the value you wish to modify. Emtpy string means the top of
  79. - the document.
  80. required: false
  81. default: ''
  82. aliases: []
  83. value:
  84. description:
  85. - The incoming value of parameter 'key'.
  86. required: false
  87. default:
  88. aliases: []
  89. value_type:
  90. description:
  91. - The python type of the incoming value.
  92. required: false
  93. default: ''
  94. aliases: []
  95. update:
  96. description:
  97. - Whether the update should be performed on a dict/hash or list/array
  98. - object.
  99. required: false
  100. default: false
  101. aliases: []
  102. append:
  103. description:
  104. - Whether to append to an array/list. When the key does not exist or is
  105. - null, a new array is created. When the key is of a non-list type,
  106. - nothing is done.
  107. required: false
  108. default: false
  109. aliases: []
  110. index:
  111. description:
  112. - Used in conjunction with the update parameter. This will update a
  113. - specific index in an array/list.
  114. required: false
  115. default: false
  116. aliases: []
  117. curr_value:
  118. description:
  119. - Used in conjunction with the update parameter. This is the current
  120. - value of 'key' in the yaml file.
  121. required: false
  122. default: false
  123. aliases: []
  124. curr_value_format:
  125. description:
  126. - Format of the incoming current value.
  127. choices: ["yaml", "json", "str"]
  128. required: false
  129. default: false
  130. aliases: []
  131. backup:
  132. description:
  133. - Whether to make a backup copy of the current file when performing an
  134. - edit.
  135. required: false
  136. default: true
  137. aliases: []
  138. separator:
  139. description:
  140. - The separator being used when parsing strings.
  141. required: false
  142. default: '.'
  143. aliases: []
  144. author:
  145. - "Kenny Woodson <kwoodson@redhat.com>"
  146. extends_documentation_fragment: []
  147. '''
  148. EXAMPLES = '''
  149. # Simple insert of key, value
  150. - name: insert simple key, value
  151. yedit:
  152. src: somefile.yml
  153. key: test
  154. value: somevalue
  155. state: present
  156. # Results:
  157. # test: somevalue
  158. # Multilevel insert of key, value
  159. - name: insert simple key, value
  160. yedit:
  161. src: somefile.yml
  162. key: a#b#c
  163. value: d
  164. state: present
  165. # Results:
  166. # a:
  167. # b:
  168. # c: d
  169. '''
  170. # -*- -*- -*- End included fragment: doc/yedit -*- -*- -*-
  171. # -*- -*- -*- Begin included fragment: class/yedit.py -*- -*- -*-
  172. # noqa: E301,E302
  173. class YeditException(Exception):
  174. ''' Exception class for Yedit '''
  175. pass
  176. # pylint: disable=too-many-public-methods
  177. class Yedit(object):
  178. ''' Class to modify yaml files '''
  179. re_valid_key = r"(((\[-?\d+\])|([0-9a-zA-Z%s/_-]+)).?)+$"
  180. re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z%s/_-]+)"
  181. com_sep = set(['.', '#', '|', ':'])
  182. # pylint: disable=too-many-arguments
  183. def __init__(self,
  184. filename=None,
  185. content=None,
  186. content_type='yaml',
  187. separator='.',
  188. backup=False):
  189. self.content = content
  190. self._separator = separator
  191. self.filename = filename
  192. self.__yaml_dict = content
  193. self.content_type = content_type
  194. self.backup = backup
  195. self.load(content_type=self.content_type)
  196. if self.__yaml_dict is None:
  197. self.__yaml_dict = {}
  198. @property
  199. def separator(self):
  200. ''' getter method for yaml_dict '''
  201. return self._separator
  202. @separator.setter
  203. def separator(self):
  204. ''' getter method for yaml_dict '''
  205. return self._separator
  206. @property
  207. def yaml_dict(self):
  208. ''' getter method for yaml_dict '''
  209. return self.__yaml_dict
  210. @yaml_dict.setter
  211. def yaml_dict(self, value):
  212. ''' setter method for yaml_dict '''
  213. self.__yaml_dict = value
  214. @staticmethod
  215. def parse_key(key, sep='.'):
  216. '''parse the key allowing the appropriate separator'''
  217. common_separators = list(Yedit.com_sep - set([sep]))
  218. return re.findall(Yedit.re_key % ''.join(common_separators), key)
  219. @staticmethod
  220. def valid_key(key, sep='.'):
  221. '''validate the incoming key'''
  222. common_separators = list(Yedit.com_sep - set([sep]))
  223. if not re.match(Yedit.re_valid_key % ''.join(common_separators), key):
  224. return False
  225. return True
  226. @staticmethod
  227. def remove_entry(data, key, sep='.'):
  228. ''' remove data at location key '''
  229. if key == '' and isinstance(data, dict):
  230. data.clear()
  231. return True
  232. elif key == '' and isinstance(data, list):
  233. del data[:]
  234. return True
  235. if not (key and Yedit.valid_key(key, sep)) and \
  236. isinstance(data, (list, dict)):
  237. return None
  238. key_indexes = Yedit.parse_key(key, sep)
  239. for arr_ind, dict_key in key_indexes[:-1]:
  240. if dict_key and isinstance(data, dict):
  241. data = data.get(dict_key, None)
  242. elif (arr_ind and isinstance(data, list) and
  243. int(arr_ind) <= len(data) - 1):
  244. data = data[int(arr_ind)]
  245. else:
  246. return None
  247. # process last index for remove
  248. # expected list entry
  249. if key_indexes[-1][0]:
  250. if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501
  251. del data[int(key_indexes[-1][0])]
  252. return True
  253. # expected dict entry
  254. elif key_indexes[-1][1]:
  255. if isinstance(data, dict):
  256. del data[key_indexes[-1][1]]
  257. return True
  258. @staticmethod
  259. def add_entry(data, key, item=None, sep='.'):
  260. ''' Get an item from a dictionary with key notation a.b.c
  261. d = {'a': {'b': 'c'}}}
  262. key = a#b
  263. return c
  264. '''
  265. if key == '':
  266. pass
  267. elif (not (key and Yedit.valid_key(key, sep)) and
  268. isinstance(data, (list, dict))):
  269. return None
  270. key_indexes = Yedit.parse_key(key, sep)
  271. for arr_ind, dict_key in key_indexes[:-1]:
  272. if dict_key:
  273. if isinstance(data, dict) and dict_key in data and data[dict_key]: # noqa: E501
  274. data = data[dict_key]
  275. continue
  276. elif data and not isinstance(data, dict):
  277. return None
  278. data[dict_key] = {}
  279. data = data[dict_key]
  280. elif (arr_ind and isinstance(data, list) and
  281. int(arr_ind) <= len(data) - 1):
  282. data = data[int(arr_ind)]
  283. else:
  284. return None
  285. if key == '':
  286. data = item
  287. # process last index for add
  288. # expected list entry
  289. elif key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501
  290. data[int(key_indexes[-1][0])] = item
  291. # expected dict entry
  292. elif key_indexes[-1][1] and isinstance(data, dict):
  293. data[key_indexes[-1][1]] = item
  294. return data
  295. @staticmethod
  296. def get_entry(data, key, sep='.'):
  297. ''' Get an item from a dictionary with key notation a.b.c
  298. d = {'a': {'b': 'c'}}}
  299. key = a.b
  300. return c
  301. '''
  302. if key == '':
  303. pass
  304. elif (not (key and Yedit.valid_key(key, sep)) and
  305. isinstance(data, (list, dict))):
  306. return None
  307. key_indexes = Yedit.parse_key(key, sep)
  308. for arr_ind, dict_key in key_indexes:
  309. if dict_key and isinstance(data, dict):
  310. data = data.get(dict_key, None)
  311. elif (arr_ind and isinstance(data, list) and
  312. int(arr_ind) <= len(data) - 1):
  313. data = data[int(arr_ind)]
  314. else:
  315. return None
  316. return data
  317. def write(self):
  318. ''' write to file '''
  319. if not self.filename:
  320. raise YeditException('Please specify a filename.')
  321. if self.backup and self.file_exists():
  322. shutil.copy(self.filename, self.filename + '.orig')
  323. tmp_filename = self.filename + '.yedit'
  324. with open(tmp_filename, 'w') as yfd:
  325. # pylint: disable=no-member
  326. if hasattr(self.yaml_dict, 'fa'):
  327. self.yaml_dict.fa.set_block_style()
  328. yfd.write(yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper))
  329. os.rename(tmp_filename, self.filename)
  330. return (True, self.yaml_dict)
  331. def read(self):
  332. ''' read from file '''
  333. # check if it exists
  334. if self.filename is None or not self.file_exists():
  335. return None
  336. contents = None
  337. with open(self.filename) as yfd:
  338. contents = yfd.read()
  339. return contents
  340. def file_exists(self):
  341. ''' return whether file exists '''
  342. if os.path.exists(self.filename):
  343. return True
  344. return False
  345. def load(self, content_type='yaml'):
  346. ''' return yaml file '''
  347. contents = self.read()
  348. if not contents and not self.content:
  349. return None
  350. if self.content:
  351. if isinstance(self.content, dict):
  352. self.yaml_dict = self.content
  353. return self.yaml_dict
  354. elif isinstance(self.content, str):
  355. contents = self.content
  356. # check if it is yaml
  357. try:
  358. if content_type == 'yaml' and contents:
  359. self.yaml_dict = yaml.load(contents, yaml.RoundTripLoader)
  360. # pylint: disable=no-member
  361. if hasattr(self.yaml_dict, 'fa'):
  362. self.yaml_dict.fa.set_block_style()
  363. elif content_type == 'json' and contents:
  364. self.yaml_dict = json.loads(contents)
  365. except yaml.YAMLError as err:
  366. # Error loading yaml or json
  367. raise YeditException('Problem with loading yaml file. %s' % err)
  368. return self.yaml_dict
  369. def get(self, key):
  370. ''' get a specified key'''
  371. try:
  372. entry = Yedit.get_entry(self.yaml_dict, key, self.separator)
  373. except KeyError:
  374. entry = None
  375. return entry
  376. def pop(self, path, key_or_item):
  377. ''' remove a key, value pair from a dict or an item for a list'''
  378. try:
  379. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  380. except KeyError:
  381. entry = None
  382. if entry is None:
  383. return (False, self.yaml_dict)
  384. if isinstance(entry, dict):
  385. # pylint: disable=no-member,maybe-no-member
  386. if key_or_item in entry:
  387. entry.pop(key_or_item)
  388. return (True, self.yaml_dict)
  389. return (False, self.yaml_dict)
  390. elif isinstance(entry, list):
  391. # pylint: disable=no-member,maybe-no-member
  392. ind = None
  393. try:
  394. ind = entry.index(key_or_item)
  395. except ValueError:
  396. return (False, self.yaml_dict)
  397. entry.pop(ind)
  398. return (True, self.yaml_dict)
  399. return (False, self.yaml_dict)
  400. def delete(self, path):
  401. ''' remove path from a dict'''
  402. try:
  403. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  404. except KeyError:
  405. entry = None
  406. if entry is None:
  407. return (False, self.yaml_dict)
  408. result = Yedit.remove_entry(self.yaml_dict, path, self.separator)
  409. if not result:
  410. return (False, self.yaml_dict)
  411. return (True, self.yaml_dict)
  412. def exists(self, path, value):
  413. ''' check if value exists at path'''
  414. try:
  415. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  416. except KeyError:
  417. entry = None
  418. if isinstance(entry, list):
  419. if value in entry:
  420. return True
  421. return False
  422. elif isinstance(entry, dict):
  423. if isinstance(value, dict):
  424. rval = False
  425. for key, val in value.items():
  426. if entry[key] != val:
  427. rval = False
  428. break
  429. else:
  430. rval = True
  431. return rval
  432. return value in entry
  433. return entry == value
  434. def append(self, path, value):
  435. '''append value to a list'''
  436. try:
  437. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  438. except KeyError:
  439. entry = None
  440. if entry is None:
  441. self.put(path, [])
  442. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  443. if not isinstance(entry, list):
  444. return (False, self.yaml_dict)
  445. # pylint: disable=no-member,maybe-no-member
  446. entry.append(value)
  447. return (True, self.yaml_dict)
  448. # pylint: disable=too-many-arguments
  449. def update(self, path, value, index=None, curr_value=None):
  450. ''' put path, value into a dict '''
  451. try:
  452. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  453. except KeyError:
  454. entry = None
  455. if isinstance(entry, dict):
  456. # pylint: disable=no-member,maybe-no-member
  457. if not isinstance(value, dict):
  458. raise YeditException('Cannot replace key, value entry in ' +
  459. 'dict with non-dict type. value=[%s] [%s]' % (value, type(value))) # noqa: E501
  460. entry.update(value)
  461. return (True, self.yaml_dict)
  462. elif isinstance(entry, list):
  463. # pylint: disable=no-member,maybe-no-member
  464. ind = None
  465. if curr_value:
  466. try:
  467. ind = entry.index(curr_value)
  468. except ValueError:
  469. return (False, self.yaml_dict)
  470. elif index is not None:
  471. ind = index
  472. if ind is not None and entry[ind] != value:
  473. entry[ind] = value
  474. return (True, self.yaml_dict)
  475. # see if it exists in the list
  476. try:
  477. ind = entry.index(value)
  478. except ValueError:
  479. # doesn't exist, append it
  480. entry.append(value)
  481. return (True, self.yaml_dict)
  482. # already exists, return
  483. if ind is not None:
  484. return (False, self.yaml_dict)
  485. return (False, self.yaml_dict)
  486. def put(self, path, value):
  487. ''' put path, value into a dict '''
  488. try:
  489. entry = Yedit.get_entry(self.yaml_dict, path, self.separator)
  490. except KeyError:
  491. entry = None
  492. if entry == value:
  493. return (False, self.yaml_dict)
  494. # deepcopy didn't work
  495. tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict,
  496. default_flow_style=False),
  497. yaml.RoundTripLoader)
  498. # pylint: disable=no-member
  499. if hasattr(self.yaml_dict, 'fa'):
  500. tmp_copy.fa.set_block_style()
  501. result = Yedit.add_entry(tmp_copy, path, value, self.separator)
  502. if not result:
  503. return (False, self.yaml_dict)
  504. self.yaml_dict = tmp_copy
  505. return (True, self.yaml_dict)
  506. def create(self, path, value):
  507. ''' create a yaml file '''
  508. if not self.file_exists():
  509. # deepcopy didn't work
  510. tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, default_flow_style=False), # noqa: E501
  511. yaml.RoundTripLoader)
  512. # pylint: disable=no-member
  513. if hasattr(self.yaml_dict, 'fa'):
  514. tmp_copy.fa.set_block_style()
  515. result = Yedit.add_entry(tmp_copy, path, value, self.separator)
  516. if result:
  517. self.yaml_dict = tmp_copy
  518. return (True, self.yaml_dict)
  519. return (False, self.yaml_dict)
  520. @staticmethod
  521. def get_curr_value(invalue, val_type):
  522. '''return the current value'''
  523. if invalue is None:
  524. return None
  525. curr_value = invalue
  526. if val_type == 'yaml':
  527. curr_value = yaml.load(invalue)
  528. elif val_type == 'json':
  529. curr_value = json.loads(invalue)
  530. return curr_value
  531. @staticmethod
  532. def parse_value(inc_value, vtype=''):
  533. '''determine value type passed'''
  534. true_bools = ['y', 'Y', 'yes', 'Yes', 'YES', 'true', 'True', 'TRUE',
  535. 'on', 'On', 'ON', ]
  536. false_bools = ['n', 'N', 'no', 'No', 'NO', 'false', 'False', 'FALSE',
  537. 'off', 'Off', 'OFF']
  538. # It came in as a string but you didn't specify value_type as string
  539. # we will convert to bool if it matches any of the above cases
  540. if isinstance(inc_value, str) and 'bool' in vtype:
  541. if inc_value not in true_bools and inc_value not in false_bools:
  542. raise YeditException('Not a boolean type. str=[%s] vtype=[%s]'
  543. % (inc_value, vtype))
  544. elif isinstance(inc_value, bool) and 'str' in vtype:
  545. inc_value = str(inc_value)
  546. # If vtype is not str then go ahead and attempt to yaml load it.
  547. if isinstance(inc_value, str) and 'str' not in vtype:
  548. try:
  549. inc_value = yaml.load(inc_value)
  550. except Exception:
  551. raise YeditException('Could not determine type of incoming ' +
  552. 'value. value=[%s] vtype=[%s]'
  553. % (type(inc_value), vtype))
  554. return inc_value
  555. # pylint: disable=too-many-return-statements,too-many-branches
  556. @staticmethod
  557. def run_ansible(module):
  558. '''perform the idempotent crud operations'''
  559. yamlfile = Yedit(filename=module.params['src'],
  560. backup=module.params['backup'],
  561. separator=module.params['separator'])
  562. if module.params['src']:
  563. rval = yamlfile.load()
  564. if yamlfile.yaml_dict is None and \
  565. module.params['state'] != 'present':
  566. return {'failed': True,
  567. 'msg': 'Error opening file [%s]. Verify that the ' +
  568. 'file exists, that it is has correct' +
  569. ' permissions, and is valid yaml.'}
  570. if module.params['state'] == 'list':
  571. if module.params['content']:
  572. content = Yedit.parse_value(module.params['content'],
  573. module.params['content_type'])
  574. yamlfile.yaml_dict = content
  575. if module.params['key']:
  576. rval = yamlfile.get(module.params['key']) or {}
  577. return {'changed': False, 'result': rval, 'state': "list"}
  578. elif module.params['state'] == 'absent':
  579. if module.params['content']:
  580. content = Yedit.parse_value(module.params['content'],
  581. module.params['content_type'])
  582. yamlfile.yaml_dict = content
  583. if module.params['update']:
  584. rval = yamlfile.pop(module.params['key'],
  585. module.params['value'])
  586. else:
  587. rval = yamlfile.delete(module.params['key'])
  588. if rval[0] and module.params['src']:
  589. yamlfile.write()
  590. return {'changed': rval[0], 'result': rval[1], 'state': "absent"}
  591. elif module.params['state'] == 'present':
  592. # check if content is different than what is in the file
  593. if module.params['content']:
  594. content = Yedit.parse_value(module.params['content'],
  595. module.params['content_type'])
  596. # We had no edits to make and the contents are the same
  597. if yamlfile.yaml_dict == content and \
  598. module.params['value'] is None:
  599. return {'changed': False,
  600. 'result': yamlfile.yaml_dict,
  601. 'state': "present"}
  602. yamlfile.yaml_dict = content
  603. # we were passed a value; parse it
  604. if module.params['value']:
  605. value = Yedit.parse_value(module.params['value'],
  606. module.params['value_type'])
  607. key = module.params['key']
  608. if module.params['update']:
  609. # pylint: disable=line-too-long
  610. curr_value = Yedit.get_curr_value(Yedit.parse_value(module.params['curr_value']), # noqa: E501
  611. module.params['curr_value_format']) # noqa: E501
  612. rval = yamlfile.update(key, value, module.params['index'], curr_value) # noqa: E501
  613. elif module.params['append']:
  614. rval = yamlfile.append(key, value)
  615. else:
  616. rval = yamlfile.put(key, value)
  617. if rval[0] and module.params['src']:
  618. yamlfile.write()
  619. return {'changed': rval[0],
  620. 'result': rval[1], 'state': "present"}
  621. # no edits to make
  622. if module.params['src']:
  623. # pylint: disable=redefined-variable-type
  624. rval = yamlfile.write()
  625. return {'changed': rval[0],
  626. 'result': rval[1],
  627. 'state': "present"}
  628. return {'failed': True, 'msg': 'Unkown state passed'}
  629. # -*- -*- -*- End included fragment: class/yedit.py -*- -*- -*-
  630. # -*- -*- -*- Begin included fragment: ansible/yedit.py -*- -*- -*-
  631. # pylint: disable=too-many-branches
  632. def main():
  633. ''' ansible oc module for secrets '''
  634. module = AnsibleModule(
  635. argument_spec=dict(
  636. state=dict(default='present', type='str',
  637. choices=['present', 'absent', 'list']),
  638. debug=dict(default=False, type='bool'),
  639. src=dict(default=None, type='str'),
  640. content=dict(default=None),
  641. content_type=dict(default='dict', choices=['dict']),
  642. key=dict(default='', type='str'),
  643. value=dict(),
  644. value_type=dict(default='', type='str'),
  645. update=dict(default=False, type='bool'),
  646. append=dict(default=False, type='bool'),
  647. index=dict(default=None, type='int'),
  648. curr_value=dict(default=None, type='str'),
  649. curr_value_format=dict(default='yaml',
  650. choices=['yaml', 'json', 'str'],
  651. type='str'),
  652. backup=dict(default=True, type='bool'),
  653. separator=dict(default='.', type='str'),
  654. ),
  655. mutually_exclusive=[["curr_value", "index"], ['update', "append"]],
  656. required_one_of=[["content", "src"]],
  657. )
  658. rval = Yedit.run_ansible(module)
  659. if 'failed' in rval and rval['failed']:
  660. module.fail_json(**rval)
  661. module.exit_json(**rval)
  662. if __name__ == '__main__':
  663. main()
  664. # -*- -*- -*- End included fragment: ansible/yedit.py -*- -*- -*-