Odyssey
ody_migr_mmth_endpoint.py
1 #!/usr/bin/env python
2 """Main script to import json data for any data category from Mammoth."""
3 
4 
5 import os
6 import requests
7 import logging
8 
9 from ody_migr_utils import generate_hash, get_valid_json
10 from ody_migr_config import (ENV_MIGR_SECRET_KEY,
11  DATA_OPT_MEMDATA,
12  DATA_OPT_SWITCH_ACCOUNTS,
13  DATA_OPT_CREATE_HIST,
14  DATA_OPT_GET_HIST,
15  DATA_OPT_GETCULIST,
16  DEFAULT_ERROR_STR)
17 
18 LOGGER = logging.getLogger(__name__)
19 
20 
22  """Retrieve response from Mammoth."""
23 
24  def __init__(self,
25  _cu,
26  _data_category,
27  _server,
28  _username,
29  _password,
30  **kwargs):
31  self.cu = _cu
32  self.data_category = _data_category
33  self.server = _server
34 
35  # being extra cautious here (execution should not reach inside this
36  # condition through the options provided in the Monitor screen)
37  if self.server == "www" and self.data_category != DATA_OPT_GETCULIST:
38  err_msg = "{} server must only be used for {} operation.".format(
39  self.server, DATA_OPT_GETCULIST)
40  LOGGER.error(err_msg)
41  raise SystemExit(err_msg)
42 
43  if self.data_category == DATA_OPT_GETCULIST:
44  # required for generating valid passphrase (consistency
45  # with Mammoth's process
46  assert self.cu == ""
47 
48  self.username = _username
49  self.password = _password
50  self.payload = None
51  self.getstream = False
52  self.histstatus = ""
53  self.endpoint_script = kwargs.get(
54  "endpoint_script", "hcuadm/mOdysseyMigrExp.prg")
55 
56  if kwargs != {}:
57  if (_data_category == DATA_OPT_MEMDATA or
58  _data_category == DATA_OPT_CREATE_HIST or
59  _data_category == DATA_OPT_SWITCH_ACCOUNTS):
60 
61  assert "params" in kwargs
62  self.payload = kwargs.get("params")
63  assert self.payload is not None
64 
65  elif _data_category == DATA_OPT_GET_HIST:
66  assert "stream" in kwargs
67  self.getstream = kwargs.get("stream")
68 
69  self.response = {}
70 
71  def _dest_url(self):
72  """Generate a Mammoth endpoint url"""
73  hash = generate_hash(self.data_category, self.cu,
74  os.environ.get(ENV_MIGR_SECRET_KEY))
75  server_dest = "https://{}.homecu.net".format(self.server)
76  if self.data_category == DATA_OPT_GETCULIST:
77  resource_dest = ("{}?"
78  "passphrase={}&"
79  "action={}").format(self.endpoint_script,
80  hash,
81  self.data_category)
82 
83  else:
84  resource_dest = ("{}?"
85  "cu={}&"
86  "passphrase={}&"
87  "action={}").format(self.endpoint_script,
88  self.cu,
89  hash,
90  self.data_category)
91  return os.path.join(server_dest, resource_dest)
92 
93  def _validate_response(self):
94  """Validate response data returned from Mammoth.
95 
96  Raises:
97  SystemExit on error
98  """
99 
100  # at this point, response dict must have already been populated
101  error_flag = False
102  resp_error = DEFAULT_ERROR_STR
103 
104  if self.data_category == DATA_OPT_GET_HIST:
105  # check for error in application/json response
106  if dict(self.response.headers)[
107  "Content-Type"].strip() == "application/json":
108  resp_error = self.response.json()["error"]
109  if resp_error != DEFAULT_ERROR_STR:
110  if "Process is already started" in resp_error:
111  # do not consider this case as error
112  if "ERROR:" in resp_error:
113  resp_error = resp_error.split("ERROR:")[1].strip()
114  self.histstatus = resp_error
115  else:
116  error_flag = True
117 
118  # check for error in text/plain response
119  if dict(self.response.headers)[
120  "Content-Type"].split(";")[0].strip() == "text/plain":
121  resp_error = self.response.content.decode()
122  if "Process is already started" in resp_error:
123  # do not consider this case as error
124  if "ERROR:" in resp_error:
125  resp_error = resp_error.split("ERROR:")[1].strip()
126  self.histstatus = resp_error
127  else:
128  error_flag = True
129 
130  else:
131  assert self.response != {}
132  resp_error = self.response["error"]
133  if resp_error != DEFAULT_ERROR_STR:
134  if "Process is already started" in resp_error:
135  # do not consider this case as error
136  if "ERROR:" in resp_error:
137  resp_error = resp_error.split("ERROR:")[1].strip()
138  self.histstatus = resp_error
139  else:
140  error_flag = True
141 
142  if error_flag:
143  LOGGER.error("Mammoth endpoint status: {}".format(resp_error))
144  raise SystemExit(
145  "Migration Request with `cu = {} and "
146  "data_category = {}` returned with error. Error: {}"
147  .format(
148  self.cu,
149  self.data_category,
150  resp_error))
151  else:
152  resp_error = "NORMAL!" if resp_error == DEFAULT_ERROR_STR \
153  else resp_error
154  LOGGER.info("Mammoth endpoint status: {}".format(resp_error))
155 
157  """Establish connection with Mammoth and obtain response object"""
158 
159  try:
160  # we run http connection within the requests context manager
161  # in order to avoid possible resource leaks
162  with requests.Session() as s:
163  url = self._dest_url()
164  if self.payload is None:
165  http_resp = s.get(
166  url,
167  auth=(self.username, self.password),
168  stream=self.getstream
169  )
170  else:
171  http_resp = s.post(
172  url,
173  auth=(self.username, self.password),
174  data=self.payload
175  )
176 
177  status_code = http_resp.status_code
178  if int(status_code) >= 300:
179  http_resp.raise_for_status()
180 
181  LOGGER.info("HTTP response url: {}".format(http_resp.url))
182  if int(status_code) == 200:
183  if self.data_category == DATA_OPT_GET_HIST:
184  self.response = http_resp
185 
186  else:
187  self.response = http_resp.json()
188 
189  except (requests.exceptions.HTTPError,
190  requests.exceptions.ConnectionError) as err:
191  LOGGER.error("Mammoth endpoint status: {}".format(err))
192  raise SystemExit(err)
193 
194  def run(self):
195  """Connect to mammoth endpoint and fetch response json data"""
197  self._validate_response()
198  return self.response
199 
200  def __str__(self):
201  return get_valid_json(self.response)