yedit.py 26 KB

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