oo_azure_rm_publish_image_facts.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #!/usr/bin/env python
  2. # pylint: disable=missing-docstring
  3. # Copyright 2018 Red Hat, Inc. and/or its affiliates
  4. # and other contributors as indicated by the @author tags.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. #
  18. from __future__ import print_function # noqa: F401
  19. # import httplib
  20. import json
  21. import os
  22. import requests
  23. from ansible.module_utils.basic import AnsibleModule
  24. class AzurePublisherException(Exception):
  25. '''Exception class for AzurePublisher'''
  26. pass
  27. class AzurePublisher(object):
  28. '''Python class to represent the Azure Publishing portal https://cloudpartner.azure.com'''
  29. # pylint: disable=too-many-arguments
  30. def __init__(self,
  31. publisher_id,
  32. client_info,
  33. ssl_verify=True,
  34. api_version='2017-10-31',
  35. debug=False):
  36. '''
  37. :publisher_id: string of the publisher id
  38. :client_info: a dict containing the client_id, client_secret to get an access_token
  39. '''
  40. self._azure_server = 'https://cloudpartner.azure.com/api/publishers/{}'.format(publisher_id)
  41. self.client_info = client_info
  42. self.ssl_verify = ssl_verify
  43. self.api_version = 'api-version={}'.format(api_version)
  44. self.debug = debug
  45. # if self.debug:
  46. # httplib.HTTPSConnection.debuglevel = 1
  47. # httplib.HTTPConnection.debuglevel = 1
  48. self._access_token = None
  49. @property
  50. def server(self):
  51. '''property for server url'''
  52. return self._azure_server
  53. @property
  54. def token(self):
  55. '''property for the access_token
  56. curl --data-urlencode "client_id=$AZURE_CLIENT_ID" \
  57. --data-urlencode "client_secret=$AZURE_CLIENT_SECRET" \
  58. --data-urlencode "grant_type=client_credentials" \
  59. --data-urlencode "resource=https://cloudpartner.azure.com" \
  60. https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/token
  61. '''
  62. if self._access_token is None:
  63. url = 'https://login.microsoftonline.com/{}/oauth2/token'.format(self.client_info['tenant_id'])
  64. data = {
  65. 'client_id': {self.client_info['client_id']},
  66. 'client_secret': self.client_info['client_secret'],
  67. 'grant_type': 'client_credentials',
  68. 'resource': 'https://cloudpartner.azure.com'
  69. }
  70. results = AzurePublisher.request('POST', url, data, {})
  71. jres = results.json()
  72. self._access_token = jres['access_token']
  73. return self._access_token
  74. def get_offers(self, offer=None, version=None, slot=''):
  75. ''' fetch all offers by publisherid '''
  76. url = '/offers'
  77. if offer is not None:
  78. url += '/{}'.format(offer)
  79. if version is not None:
  80. url += '/versions/{}'.format(version)
  81. if slot != '':
  82. url += '/slot/{}'.format(slot)
  83. url += '?{}'.format(self.api_version)
  84. return self.prepare_action(url)
  85. def get_operations(self, offer, operation=None, status=None):
  86. ''' create or modify an offer '''
  87. url = '/offers/{0}/submissions'.format(offer)
  88. if operation is not None:
  89. url += '/operations/{0}'.format(operation)
  90. if not url.endswith('/'):
  91. url += '/'
  92. url += '?{0}'.format(self.api_version)
  93. if status is not None:
  94. url += '&status={0}'.format(status)
  95. return self.prepare_action(url, 'GET')
  96. def cancel_operation(self, offer):
  97. ''' create or modify an offer '''
  98. url = '/offers/{0}/cancel?{1}'.format(offer, self.api_version)
  99. return self.prepare_action(url, 'POST')
  100. def go_live(self, offer):
  101. ''' create or modify an offer '''
  102. url = '/offers/{0}/golive?{1}'.format(offer, self.api_version)
  103. return self.prepare_action(url, 'POST')
  104. def create_or_modify_offer(self, offer, data=None, modify=False):
  105. ''' create or modify an offer '''
  106. url = '/offers/{0}?{1}'.format(offer, self.api_version)
  107. headers = None
  108. if modify:
  109. headers = {
  110. 'If-Match': '*',
  111. }
  112. return self.prepare_action(url, 'PUT', data=data, add_headers=headers)
  113. def prepare_action(self, url, action='GET', data=None, add_headers=None):
  114. '''perform the http request
  115. :action: string of either GET|POST
  116. '''
  117. headers = {
  118. 'Content-Type': 'application/json',
  119. 'Accept': 'application/json',
  120. 'Authorization': 'Bearer {}'.format(self.token)
  121. }
  122. if add_headers is not None:
  123. headers.update(add_headers)
  124. if data is None:
  125. data = ''
  126. else:
  127. data = json.dumps(data)
  128. return AzurePublisher.request(action.upper(), self.server + url, data, headers)
  129. def manage_offer(self, params):
  130. ''' handle creating or modifying offers'''
  131. # fetch the offer to verify it exists:
  132. results = self.get_offers(offer=params['offer'])
  133. if results.status_code == 200 and params['force']:
  134. return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'], modify=True)
  135. return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'])
  136. @staticmethod
  137. def request(action, url, data=None, headers=None, ssl_verify=True):
  138. req = requests.Request(action.upper(), url, data=data, headers=headers)
  139. session = requests.Session()
  140. req_prep = session.prepare_request(req)
  141. response = session.send(req_prep, verify=ssl_verify)
  142. return response
  143. @staticmethod
  144. def run_ansible(params):
  145. '''perform the ansible operations'''
  146. client_info = {
  147. 'tenant_id': params['tenant_id'],
  148. 'client_id': params['client_id'],
  149. 'client_secret': params['client_secret']}
  150. apc = AzurePublisher(params['publisher'],
  151. client_info,
  152. debug=params['debug'])
  153. if params['query'] == 'offer':
  154. results = apc.get_offers(offer=params['offer'])
  155. elif params['query'] == 'operation':
  156. results = apc.get_operations(offer=params['offer'], operation=params['operation'], status=params['status'])
  157. else:
  158. raise AzurePublisherException('Unsupported query type: {}'.format(params['query']))
  159. return {'data': results.json(), 'status_code': results.status_code}
  160. def main():
  161. ''' ansible oc module for secrets '''
  162. module = AnsibleModule(
  163. argument_spec=dict(
  164. query=dict(default='offer', choices=['offer', 'operation']),
  165. publisher=dict(default='redhat', type='str'),
  166. debug=dict(default=False, type='bool'),
  167. tenant_id=dict(default=os.environ.get('AZURE_TENANT_ID'), type='str'),
  168. client_id=dict(default=os.environ.get('AZURE_CLIENT_ID'), type='str'),
  169. client_secret=dict(default=os.environ.get('AZURE_CLIENT_SECRET'), type='str'),
  170. offer=dict(default=None, type='str'),
  171. operation=dict(default=None, type='str'),
  172. status=dict(default=None, type='str'),
  173. ),
  174. )
  175. # Verify we recieved either a valid key or edits with valid keys when receiving a src file.
  176. # A valid key being not None or not ''.
  177. if (module.params['tenant_id'] is None or module.params['client_id'] is None or
  178. module.params['client_secret'] is None):
  179. return module.fail_json(**{'failed': True,
  180. 'msg': 'Please specify tenant_id, client_id, and client_secret'})
  181. rval = AzurePublisher.run_ansible(module.params)
  182. if int(rval['status_code']) == 404:
  183. rval['msg'] = 'Offer does not exist.'
  184. elif int(rval['status_code']) >= 300:
  185. rval['msg'] = 'Error.'
  186. return module.fail_json(**rval)
  187. return module.exit_json(**rval)
  188. if __name__ == '__main__':
  189. main()