oadm_router.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. #!/usr/bin/env python
  2. # ___ ___ _ _ ___ ___ _ _____ ___ ___
  3. # / __| __| \| | __| _ \ /_\_ _| __| \
  4. # | (_ | _|| .` | _|| / / _ \| | | _|| |) |
  5. # \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
  6. # | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
  7. # | |) | (_) | | .` | (_) || | | _|| |) | | | |
  8. # |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
  9. '''
  10. OpenShiftCLI class that wraps the oc commands in a subprocess
  11. '''
  12. import atexit
  13. import json
  14. import os
  15. import shutil
  16. import subprocess
  17. import re
  18. import yaml
  19. # This is here because of a bug that causes yaml
  20. # to incorrectly handle timezone info on timestamps
  21. def timestamp_constructor(_, node):
  22. '''return timestamps as strings'''
  23. return str(node.value)
  24. yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor)
  25. # pylint: disable=too-few-public-methods
  26. class OpenShiftCLI(object):
  27. ''' Class to wrap the command line tools '''
  28. def __init__(self,
  29. namespace,
  30. kubeconfig='/etc/origin/master/admin.kubeconfig',
  31. verbose=False):
  32. ''' Constructor for OpenshiftCLI '''
  33. self.namespace = namespace
  34. self.verbose = verbose
  35. self.kubeconfig = kubeconfig
  36. # Pylint allows only 5 arguments to be passed.
  37. # pylint: disable=too-many-arguments
  38. def _replace_content(self, resource, rname, content, force=False):
  39. ''' replace the current object with the content '''
  40. res = self._get(resource, rname)
  41. if not res['results']:
  42. return res
  43. fname = '/tmp/%s' % rname
  44. yed = Yedit(fname, res['results'][0])
  45. changes = []
  46. for key, value in content.items():
  47. changes.append(yed.put(key, value))
  48. if any([not change[0] for change in changes]):
  49. return {'returncode': 0, 'updated': False}
  50. yed.write()
  51. atexit.register(Utils.cleanup, [fname])
  52. return self._replace(fname, force)
  53. def _replace(self, fname, force=False):
  54. '''return all pods '''
  55. cmd = ['-n', self.namespace, 'replace', '-f', fname]
  56. if force:
  57. cmd.append('--force')
  58. return self.openshift_cmd(cmd)
  59. def _create(self, fname):
  60. '''return all pods '''
  61. return self.openshift_cmd(['create', '-f', fname, '-n', self.namespace])
  62. def _delete(self, resource, rname):
  63. '''return all pods '''
  64. return self.openshift_cmd(['delete', resource, rname, '-n', self.namespace])
  65. def _get(self, resource, rname=None):
  66. '''return a secret by name '''
  67. cmd = ['get', resource, '-o', 'json', '-n', self.namespace]
  68. if rname:
  69. cmd.append(rname)
  70. rval = self.openshift_cmd(cmd, output=True)
  71. # Ensure results are retuned in an array
  72. if rval.has_key('items'):
  73. rval['results'] = rval['items']
  74. elif not isinstance(rval['results'], list):
  75. rval['results'] = [rval['results']]
  76. return rval
  77. def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json'):
  78. '''Base command for oc '''
  79. #cmds = ['/usr/bin/oc', '--config', self.kubeconfig]
  80. cmds = []
  81. if oadm:
  82. cmds = ['/usr/bin/oadm']
  83. else:
  84. cmds = ['/usr/bin/oc']
  85. cmds.extend(cmd)
  86. rval = {}
  87. results = ''
  88. err = None
  89. if self.verbose:
  90. print ' '.join(cmds)
  91. proc = subprocess.Popen(cmds,
  92. stdout=subprocess.PIPE,
  93. stderr=subprocess.PIPE,
  94. env={'KUBECONFIG': self.kubeconfig})
  95. proc.wait()
  96. stdout = proc.stdout.read()
  97. stderr = proc.stderr.read()
  98. rval = {"returncode": proc.returncode,
  99. "results": results,
  100. "cmd": ' '.join(cmds),
  101. }
  102. if proc.returncode == 0:
  103. if output:
  104. if output_type == 'json':
  105. try:
  106. rval['results'] = json.loads(stdout)
  107. except ValueError as err:
  108. if "No JSON object could be decoded" in err.message:
  109. err = err.message
  110. elif output_type == 'raw':
  111. rval['results'] = stdout
  112. if self.verbose:
  113. print stdout
  114. print stderr
  115. print
  116. if err:
  117. rval.update({"err": err,
  118. "stderr": stderr,
  119. "stdout": stdout,
  120. "cmd": cmds
  121. })
  122. else:
  123. rval.update({"stderr": stderr,
  124. "stdout": stdout,
  125. "results": {},
  126. })
  127. return rval
  128. class Utils(object):
  129. ''' utilities for openshiftcli modules '''
  130. @staticmethod
  131. def create_file(rname, data, ftype=None):
  132. ''' create a file in tmp with name and contents'''
  133. path = os.path.join('/tmp', rname)
  134. with open(path, 'w') as fds:
  135. if ftype == 'yaml':
  136. fds.write(yaml.safe_dump(data, default_flow_style=False))
  137. elif ftype == 'json':
  138. fds.write(json.dumps(data))
  139. else:
  140. fds.write(data)
  141. # Register cleanup when module is done
  142. atexit.register(Utils.cleanup, [path])
  143. return path
  144. @staticmethod
  145. def create_files_from_contents(data):
  146. '''Turn an array of dict: filename, content into a files array'''
  147. files = []
  148. for sfile in data:
  149. path = Utils.create_file(sfile['path'], sfile['content'])
  150. files.append(path)
  151. return files
  152. @staticmethod
  153. def cleanup(files):
  154. '''Clean up on exit '''
  155. for sfile in files:
  156. if os.path.exists(sfile):
  157. if os.path.isdir(sfile):
  158. shutil.rmtree(sfile)
  159. elif os.path.isfile(sfile):
  160. os.remove(sfile)
  161. @staticmethod
  162. def exists(results, _name):
  163. ''' Check to see if the results include the name '''
  164. if not results:
  165. return False
  166. if Utils.find_result(results, _name):
  167. return True
  168. return False
  169. @staticmethod
  170. def find_result(results, _name):
  171. ''' Find the specified result by name'''
  172. rval = None
  173. for result in results:
  174. if result.has_key('metadata') and result['metadata']['name'] == _name:
  175. rval = result
  176. break
  177. return rval
  178. @staticmethod
  179. def get_resource_file(sfile, sfile_type='yaml'):
  180. ''' return the service file '''
  181. contents = None
  182. with open(sfile) as sfd:
  183. contents = sfd.read()
  184. if sfile_type == 'yaml':
  185. contents = yaml.safe_load(contents)
  186. elif sfile_type == 'json':
  187. contents = json.loads(contents)
  188. return contents
  189. # Disabling too-many-branches. This is a yaml dictionary comparison function
  190. # pylint: disable=too-many-branches,too-many-return-statements
  191. @staticmethod
  192. def check_def_equal(user_def, result_def, skip_keys=None, debug=False):
  193. ''' Given a user defined definition, compare it with the results given back by our query. '''
  194. # Currently these values are autogenerated and we do not need to check them
  195. skip = ['metadata', 'status']
  196. if skip_keys:
  197. skip.extend(skip_keys)
  198. for key, value in result_def.items():
  199. if key in skip:
  200. continue
  201. # Both are lists
  202. if isinstance(value, list):
  203. if not isinstance(user_def[key], list):
  204. if debug:
  205. print 'user_def[key] is not a list'
  206. return False
  207. for values in zip(user_def[key], value):
  208. if isinstance(values[0], dict) and isinstance(values[1], dict):
  209. if debug:
  210. print 'sending list - list'
  211. print type(values[0])
  212. print type(values[1])
  213. result = Utils.check_def_equal(values[0], values[1], skip_keys=skip_keys, debug=debug)
  214. if not result:
  215. print 'list compare returned false'
  216. return False
  217. elif value != user_def[key]:
  218. if debug:
  219. print 'value should be identical'
  220. print value
  221. print user_def[key]
  222. return False
  223. # recurse on a dictionary
  224. elif isinstance(value, dict):
  225. if not isinstance(user_def[key], dict):
  226. if debug:
  227. print "dict returned false not instance of dict"
  228. return False
  229. # before passing ensure keys match
  230. api_values = set(value.keys()) - set(skip)
  231. user_values = set(user_def[key].keys()) - set(skip)
  232. if api_values != user_values:
  233. if debug:
  234. print api_values
  235. print user_values
  236. print "keys are not equal in dict"
  237. return False
  238. result = Utils.check_def_equal(user_def[key], value, skip_keys=skip_keys, debug=debug)
  239. if not result:
  240. if debug:
  241. print "dict returned false"
  242. print result
  243. return False
  244. # Verify each key, value pair is the same
  245. else:
  246. if not user_def.has_key(key) or value != user_def[key]:
  247. if debug:
  248. print "value not equal; user_def does not have key"
  249. print value
  250. print user_def[key]
  251. return False
  252. return True
  253. class YeditException(Exception):
  254. ''' Exception class for Yedit '''
  255. pass
  256. class Yedit(object):
  257. ''' Class to modify yaml files '''
  258. re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$"
  259. re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)"
  260. def __init__(self, filename=None, content=None, content_type='yaml'):
  261. self.content = content
  262. self.filename = filename
  263. self.__yaml_dict = content
  264. self.content_type = content_type
  265. if self.filename and not self.content:
  266. self.load(content_type=self.content_type)
  267. @property
  268. def yaml_dict(self):
  269. ''' getter method for yaml_dict '''
  270. return self.__yaml_dict
  271. @yaml_dict.setter
  272. def yaml_dict(self, value):
  273. ''' setter method for yaml_dict '''
  274. self.__yaml_dict = value
  275. @staticmethod
  276. def remove_entry(data, key):
  277. ''' remove data at location key '''
  278. if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))):
  279. return None
  280. key_indexes = re.findall(Yedit.re_key, key)
  281. for arr_ind, dict_key in key_indexes[:-1]:
  282. if dict_key and isinstance(data, dict):
  283. data = data.get(dict_key, None)
  284. elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1:
  285. data = data[int(arr_ind)]
  286. else:
  287. return None
  288. # process last index for remove
  289. # expected list entry
  290. if key_indexes[-1][0]:
  291. if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1:
  292. del data[int(key_indexes[-1][0])]
  293. return True
  294. # expected dict entry
  295. elif key_indexes[-1][1]:
  296. if isinstance(data, dict):
  297. del data[key_indexes[-1][1]]
  298. return True
  299. @staticmethod
  300. def add_entry(data, key, item=None):
  301. ''' Get an item from a dictionary with key notation a.b.c
  302. d = {'a': {'b': 'c'}}}
  303. key = a.b
  304. return c
  305. '''
  306. if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))):
  307. return None
  308. curr_data = data
  309. key_indexes = re.findall(Yedit.re_key, key)
  310. for arr_ind, dict_key in key_indexes[:-1]:
  311. if dict_key:
  312. if isinstance(data, dict) and data.has_key(dict_key):
  313. data = data[dict_key]
  314. continue
  315. data[dict_key] = {}
  316. data = data[dict_key]
  317. elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1:
  318. data = data[int(arr_ind)]
  319. else:
  320. return None
  321. # process last index for add
  322. # expected list entry
  323. if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1:
  324. data[int(key_indexes[-1][0])] = item
  325. # expected dict entry
  326. elif key_indexes[-1][1] and isinstance(data, dict):
  327. data[key_indexes[-1][1]] = item
  328. return curr_data
  329. @staticmethod
  330. def get_entry(data, key):
  331. ''' Get an item from a dictionary with key notation a.b.c
  332. d = {'a': {'b': 'c'}}}
  333. key = a.b
  334. return c
  335. '''
  336. if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))):
  337. return None
  338. key_indexes = re.findall(Yedit.re_key, key)
  339. for arr_ind, dict_key in key_indexes:
  340. if dict_key and isinstance(data, dict):
  341. data = data.get(dict_key, None)
  342. elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1:
  343. data = data[int(arr_ind)]
  344. else:
  345. return None
  346. return data
  347. def write(self):
  348. ''' write to file '''
  349. if not self.filename:
  350. raise YeditException('Please specify a filename.')
  351. with open(self.filename, 'w') as yfd:
  352. yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
  353. def read(self):
  354. ''' write to file '''
  355. # check if it exists
  356. if not self.exists():
  357. return None
  358. contents = None
  359. with open(self.filename) as yfd:
  360. contents = yfd.read()
  361. return contents
  362. def exists(self):
  363. ''' return whether file exists '''
  364. if os.path.exists(self.filename):
  365. return True
  366. return False
  367. def load(self, content_type='yaml'):
  368. ''' return yaml file '''
  369. contents = self.read()
  370. if not contents:
  371. return None
  372. # check if it is yaml
  373. try:
  374. if content_type == 'yaml':
  375. self.yaml_dict = yaml.load(contents)
  376. elif content_type == 'json':
  377. self.yaml_dict = json.loads(contents)
  378. except yaml.YAMLError as _:
  379. # Error loading yaml or json
  380. return None
  381. return self.yaml_dict
  382. def get(self, key):
  383. ''' get a specified key'''
  384. try:
  385. entry = Yedit.get_entry(self.yaml_dict, key)
  386. except KeyError as _:
  387. entry = None
  388. return entry
  389. def delete(self, key):
  390. ''' remove key from a dict'''
  391. try:
  392. entry = Yedit.get_entry(self.yaml_dict, key)
  393. except KeyError as _:
  394. entry = None
  395. if not entry:
  396. return (False, self.yaml_dict)
  397. result = Yedit.remove_entry(self.yaml_dict, key)
  398. if not result:
  399. return (False, self.yaml_dict)
  400. return (True, self.yaml_dict)
  401. def put(self, key, value):
  402. ''' put key, value into a dict '''
  403. try:
  404. entry = Yedit.get_entry(self.yaml_dict, key)
  405. except KeyError as _:
  406. entry = None
  407. if entry == value:
  408. return (False, self.yaml_dict)
  409. result = Yedit.add_entry(self.yaml_dict, key, value)
  410. if not result:
  411. return (False, self.yaml_dict)
  412. return (True, self.yaml_dict)
  413. def create(self, key, value):
  414. ''' create a yaml file '''
  415. if not self.exists():
  416. self.yaml_dict = {key: value}
  417. return (True, self.yaml_dict)
  418. return (False, self.yaml_dict)
  419. import time
  420. class RouterConfig(object):
  421. ''' RouterConfig is a DTO for the router. '''
  422. def __init__(self, rname, kubeconfig, router_options):
  423. self.name = rname
  424. self.kubeconfig = kubeconfig
  425. self._router_options = router_options
  426. @property
  427. def router_options(self):
  428. ''' return router options '''
  429. return self._router_options
  430. def to_option_list(self):
  431. ''' return all options as a string'''
  432. return RouterConfig.stringify(self.router_options)
  433. @staticmethod
  434. def stringify(options):
  435. ''' return hash as list of key value pairs '''
  436. rval = []
  437. for key, data in options.items():
  438. if data['include'] and data['value']:
  439. rval.append('--%s=%s' % (key.replace('_', '-'), data['value']))
  440. return rval
  441. class Router(OpenShiftCLI):
  442. ''' Class to wrap the oc command line tools '''
  443. def __init__(self,
  444. router_config,
  445. verbose=False):
  446. ''' Constructor for OpenshiftOC
  447. a router consists of 3 or more parts
  448. - dc/router
  449. - svc/router
  450. - endpoint/router
  451. '''
  452. super(Router, self).__init__('default', router_config.kubeconfig, verbose)
  453. self.rconfig = router_config
  454. self.verbose = verbose
  455. self.router_parts = [{'kind': 'dc', 'name': self.rconfig.name},
  456. {'kind': 'svc', 'name': self.rconfig.name},
  457. #{'kind': 'endpoints', 'name': self.rconfig.name},
  458. ]
  459. def get(self, filter_kind=None):
  460. ''' return the self.router_parts '''
  461. rparts = self.router_parts
  462. parts = []
  463. if filter_kind:
  464. rparts = [part for part in self.router_parts if filter_kind == part['kind']]
  465. for part in rparts:
  466. parts.append(self._get(part['kind'], rname=part['name']))
  467. return parts
  468. def exists(self):
  469. '''return a deploymentconfig by name '''
  470. parts = self.get()
  471. for part in parts:
  472. if part['returncode'] != 0:
  473. return False
  474. return True
  475. def delete(self):
  476. '''return all pods '''
  477. parts = []
  478. for part in self.router_parts:
  479. parts.append(self._delete(part['kind'], part['name']))
  480. return parts
  481. def create(self, dryrun=False, output=False, output_type='json'):
  482. '''Create a deploymentconfig '''
  483. # We need to create the pem file
  484. router_pem = '/tmp/router.pem'
  485. with open(router_pem, 'w') as rfd:
  486. rfd.write(open(self.rconfig.router_options['cert_file']['value']).read())
  487. rfd.write(open(self.rconfig.router_options['key_file']['value']).read())
  488. atexit.register(Utils.cleanup, [router_pem])
  489. self.rconfig.router_options['default_cert']['value'] = router_pem
  490. options = self.rconfig.to_option_list()
  491. cmd = ['router']
  492. cmd.extend(options)
  493. if dryrun:
  494. cmd.extend(['--dry-run=True', '-o', 'json'])
  495. results = self.openshift_cmd(cmd, oadm=True, output=output, output_type=output_type)
  496. return results
  497. def update(self):
  498. '''run update for the router. This performs a delete and then create '''
  499. parts = self.delete()
  500. if any([part['returncode'] != 0 for part in parts]):
  501. return parts
  502. # Ugly built in sleep here.
  503. time.sleep(15)
  504. return self.create()
  505. def needs_update(self, verbose=False):
  506. ''' check to see if we need to update '''
  507. dc_inmem = self.get(filter_kind='dc')[0]
  508. if dc_inmem['returncode'] != 0:
  509. return dc_inmem
  510. user_dc = self.create(dryrun=True, output=True, output_type='raw')
  511. if user_dc['returncode'] != 0:
  512. return user_dc
  513. # Since the output from oadm_router is returned as raw
  514. # we need to parse it. The first line is the stats_password
  515. user_dc_results = user_dc['results'].split('\n')
  516. # stats_password = user_dc_results[0]
  517. # Load the string back into json and get the newly created dc
  518. user_dc = json.loads('\n'.join(user_dc_results[1:]))['items'][0]
  519. # Router needs some exceptions.
  520. # We do not want to check the autogenerated password for stats admin
  521. if not self.rconfig.router_options['stats_password']['value']:
  522. for idx, env_var in enumerate(user_dc['spec']['template']['spec']['containers'][0]['env']):
  523. if env_var['name'] == 'STATS_PASSWORD':
  524. env_var['value'] = \
  525. dc_inmem['results'][0]['spec']['template']['spec']['containers'][0]['env'][idx]['value']
  526. # dry-run doesn't add the protocol to the ports section. We will manually do that.
  527. for idx, port in enumerate(user_dc['spec']['template']['spec']['containers'][0]['ports']):
  528. if not port.has_key('protocol'):
  529. port['protocol'] = 'TCP'
  530. # These are different when generating
  531. skip = ['dnsPolicy',
  532. 'terminationGracePeriodSeconds',
  533. 'restartPolicy', 'timeoutSeconds',
  534. 'livenessProbe', 'readinessProbe',
  535. 'terminationMessagePath',
  536. 'rollingParams',
  537. ]
  538. return not Utils.check_def_equal(user_dc, dc_inmem['results'][0], skip_keys=skip, debug=verbose)
  539. def main():
  540. '''
  541. ansible oc module for secrets
  542. '''
  543. module = AnsibleModule(
  544. argument_spec=dict(
  545. state=dict(default='present', type='str',
  546. choices=['present', 'absent']),
  547. debug=dict(default=False, type='bool'),
  548. namespace=dict(default='default', type='str'),
  549. name=dict(default='router', type='str'),
  550. kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'),
  551. credentials=dict(default='/etc/origin/master/openshift-router.kubeconfig', type='str'),
  552. cert_file=dict(default=None, type='str'),
  553. key_file=dict(default=None, type='str'),
  554. image=dict(default=None, type='str'), #'openshift3/ose-${component}:${version}'
  555. latest_image=dict(default=False, type='bool'),
  556. labels=dict(default=None, type='list'),
  557. ports=dict(default=['80:80', '443:443'], type='list'),
  558. replicas=dict(default=1, type='int'),
  559. selector=dict(default=None, type='str'),
  560. service_account=dict(default='router', type='str'),
  561. router_type=dict(default='haproxy-router', type='str'),
  562. host_network=dict(default=True, type='bool'),
  563. # external host options
  564. external_host=dict(default=None, type='str'),
  565. external_host_vserver=dict(default=None, type='str'),
  566. external_host_insecure=dict(default=False, type='bool'),
  567. external_host_partition_path=dict(default=None, type='str'),
  568. external_host_username=dict(default=None, type='str'),
  569. external_host_password=dict(default=None, type='str'),
  570. external_host_private_key=dict(default=None, type='str'),
  571. # Metrics
  572. expose_metrics=dict(default=False, type='bool'),
  573. metrics_image=dict(default=None, type='str'),
  574. # Stats
  575. stats_user=dict(default=None, type='str'),
  576. stats_password=dict(default=None, type='str'),
  577. stats_port=dict(default=1936, type='int'),
  578. ),
  579. mutually_exclusive=[["router_type", "images"]],
  580. supports_check_mode=True,
  581. )
  582. rconfig = RouterConfig(module.params['name'],
  583. module.params['kubeconfig'],
  584. {'credentials': {'value': module.params['credentials'], 'include': True},
  585. 'default_cert': {'value': None, 'include': True},
  586. 'cert_file': {'value': module.params['cert_file'], 'include': False},
  587. 'key_file': {'value': module.params['key_file'], 'include': False},
  588. 'image': {'value': module.params['image'], 'include': True},
  589. 'latest_image': {'value': module.params['latest_image'], 'include': True},
  590. 'labels': {'value': module.params['labels'], 'include': True},
  591. 'ports': {'value': ','.join(module.params['ports']), 'include': True},
  592. 'replicas': {'value': module.params['replicas'], 'include': True},
  593. 'selector': {'value': module.params['selector'], 'include': True},
  594. 'service_account': {'value': module.params['service_account'], 'include': True},
  595. 'router_type': {'value': module.params['router_type'], 'include': False},
  596. 'host_network': {'value': module.params['host_network'], 'include': True},
  597. 'external_host': {'value': module.params['external_host'], 'include': True},
  598. 'external_host_vserver': {'value': module.params['external_host_vserver'],
  599. 'include': True},
  600. 'external_host_insecure': {'value': module.params['external_host_insecure'],
  601. 'include': True},
  602. 'external_host_partition_path': {'value': module.params['external_host_partition_path'],
  603. 'include': True},
  604. 'external_host_username': {'value': module.params['external_host_username'],
  605. 'include': True},
  606. 'external_host_password': {'value': module.params['external_host_password'],
  607. 'include': True},
  608. 'external_host_private_key': {'value': module.params['external_host_private_key'],
  609. 'include': True},
  610. 'expose_metrics': {'value': module.params['expose_metrics'], 'include': True},
  611. 'metrics_image': {'value': module.params['metrics_image'], 'include': True},
  612. 'stats_user': {'value': module.params['stats_user'], 'include': True},
  613. 'stats_password': {'value': module.params['stats_password'], 'include': True},
  614. 'stats_port': {'value': module.params['stats_port'], 'include': True},
  615. })
  616. ocrouter = Router(rconfig)
  617. state = module.params['state']
  618. ########
  619. # Delete
  620. ########
  621. if state == 'absent':
  622. if not ocrouter.exists():
  623. module.exit_json(changed=False, state="absent")
  624. if module.check_mode:
  625. module.exit_json(change=False, msg='Would have performed a delete.')
  626. api_rval = ocrouter.delete()
  627. module.exit_json(changed=True, results=api_rval, state="absent")
  628. if state == 'present':
  629. ########
  630. # Create
  631. ########
  632. if not ocrouter.exists():
  633. if module.check_mode:
  634. module.exit_json(change=False, msg='Would have performed a create.')
  635. api_rval = ocrouter.create()
  636. module.exit_json(changed=True, results=api_rval, state="present")
  637. ########
  638. # Update
  639. ########
  640. if not ocrouter.needs_update():
  641. module.exit_json(changed=False, state="present")
  642. if module.check_mode:
  643. module.exit_json(change=False, msg='Would have performed an update.')
  644. api_rval = ocrouter.update()
  645. if api_rval['returncode'] != 0:
  646. module.fail_json(msg=api_rval)
  647. module.exit_json(changed=True, results=api_rval, state="present")
  648. module.exit_json(failed=True,
  649. changed=False,
  650. results='Unknown state passed. %s' % state,
  651. state="unknown")
  652. # pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
  653. # import module snippets. This are required
  654. from ansible.module_utils.basic import *
  655. main()