Odyssey
Functions | Variables
ody_migr_executor Namespace Reference

Functions

def get_ownership_detail (_directory)
 
def owned_file_handler (_file_name, mode='w', owner=None)
 
def setup_logging (_verbose, _disable_logging, formatter, _cu, _server, _log_file)
 
def get_parser ()
 
def get_valid_cu_list ()
 
def run ()
 

Variables

 LOGGER = logging.getLogger(__name__)
 
dictionary MIGRATION_ENTRYPOINTS
 

Detailed Description

Main module to execute Mammoth to Odyssey Data Migration.

usage: ody_migr_executor.py [-h] [--secret SECRET] [--reset_and_migrate]
                [--verbose] [--disable_logging]
                {www4,www3,www5,www6} {clean,migrate,summary} cu
                {settings,memdata,memhist,admin} username password

Argument Parser for Mammoth to Odyssey Data Migration.

positional arguments:
  {www4,www3,www5,www6}
            server code for Mammoth endpoint
  {clean,migrate,summary}
            migration action
  cu                    target Credit Union <CUCODE> to migrate (upper case
            required)
  {settings,memdata,memhist,admin,loanapp}
            data category to migrate
  username              mammoth monitor username
  password              mammoth monitor password (base64 encoded)

optional arguments:
  -h, --help            show this help message and exit
  --secret SECRET, -s SECRET
            pass secret key as optional argument instead
            (alternative to environment variable)
  --reset_and_migrate, -reset
            clean db tables and commit before migration)
  --verbose, -v         output verbosity
  --disable_logging, -dl
            disable logging, logs only critical messages


Requirements:
    Python>=3.4
    Main packages:
        psycopg2>=2.7.4
        requests

Note (06/14/2018):
All the data migration scripts(app/tools/bin/ody_migr_*.py) have a shelf life.
Their usage is limited to one-time data migration of CU and its members from
Mammoth to Odyssey. We do not intend to maintain these scripts as-is after
migrations are complete.

Function Documentation

◆ get_ownership_detail()

def ody_migr_executor.get_ownership_detail (   _directory)
get file/directory user/group ownership detail

Definition at line 101 of file ody_migr_executor.py.

101 def get_ownership_detail(_directory):
102  """get file/directory user/group ownership detail"""
103  stat_info = os.stat(_directory)
104  user = pwd.getpwuid(stat_info.st_uid)[0]
105  group = grp.getgrgid(stat_info.st_gid)[0]
106  return user, group
107 
108 
109 @file_exc_decorator

◆ get_parser()

def ody_migr_executor.get_parser ( )
Argument parser for the main executor

Definition at line 170 of file ody_migr_executor.py.

170 def get_parser():
171  """Argument parser for the main executor"""
172  parser = argparse.ArgumentParser(
173  description="Argument Parser for Mammoth to Odyssey Data Migration.")
174 
175  parser.add_argument(
176  "server",
177  type=str,
178  choices=SERVER_CHOICES,
179  help="server code for Mammoth endpoint"
180  )
181 
182  parser.add_argument(
183  "action",
184  type=str,
185  choices=ACTION_CHOICES,
186  help="migration action"
187  )
188 
189  parser.add_argument(
190  "cu",
191  type=str,
192  help="target Credit Union <CUCODE> to migrate (upper case required)"
193  )
194 
195  parser.add_argument(
196  "data_category",
197  type=str,
198  choices=DATA_CHOICES,
199  help="data category to migrate"
200  )
201 
202  parser.add_argument(
203  "username",
204  type=str,
205  help="mammoth monitor username"
206  )
207 
208  parser.add_argument(
209  "password",
210  type=str,
211  help="mammoth monitor password (base64 encoded)"
212  )
213 
214  parser.add_argument(
215  "--secret",
216  "-s",
217  type=str,
218  help=("pass secret key as optional argument instead "
219  "(alternative to environment variable)")
220  )
221 
222  parser.add_argument(
223  "--reset_and_migrate",
224  "-reset",
225  help="clean db tables and commit before migration)",
226  action="store_true"
227  )
228 
229  parser.add_argument(
230  "--verbose",
231  "-v",
232  help="output verbosity",
233  action="store_true"
234  )
235 
236  parser.add_argument(
237  "--disable_logging",
238  "-dl",
239  help="disable logging, logs only critical messages",
240  action="store_true"
241  )
242 
243  return parser
244 
245 
246 @pg_crsr_hndlr_decrtr

◆ get_valid_cu_list()

def ody_migr_executor.get_valid_cu_list ( )
Return a list of valid CUs from cuinfo

Definition at line 247 of file ody_migr_executor.py.

247 def get_valid_cu_list():
248  """Return a list of valid CUs from cuinfo"""
249  sql = ("select user_name, www_server, case "
250  " when (coalesce(system_options, 0) & {}) <> 0 then 'Live'"
251  " when (coalesce(system_options, 0) & {}) <> 0 then 'Batch'"
252  " when (coalesce(system_options, 0) & {}) <> 0 then 'Web Only'"
253  " else 'Other' end as cutype from cuinfo"
254  " where (coalesce(system_options, 0) & {}) = 0"
255  " order by user_name".format(SYS_TYPE_LIVE,
256  SYS_TYPE_BATCH,
257  SYS_TYPE_WEBONLY,
258  SYS_TYPE_CLOSED))
259  valid_cu_list = []
260  with pg_handler.PGSession() as conn:
261  with conn.cursor(cursor_factory=RealDictCursor) as cur:
262  cur.execute(sql)
263  valid_cu_list = cur.fetchall()
264  return valid_cu_list
265 
266 

◆ owned_file_handler()

def ody_migr_executor.owned_file_handler (   _file_name,
  mode = 'w',
  owner = None 
)
Return a logging file handler with specified user/group ownership

Definition at line 110 of file ody_migr_executor.py.

110 def owned_file_handler(_file_name, mode='w', owner=None):
111  """Return a logging file handler with specified user/group ownership"""
112  if owner:
113  import pwd
114  import grp
115  # convert user and group names to uid and gid
116  uid = pwd.getpwnam(owner[0]).pw_uid
117  gid = grp.getgrnam(owner[1]).gr_gid
118  owner = (uid, gid)
119  if not os.path.exists(_file_name):
120  open(_file_name, 'a').close()
121  os.chown(_file_name, *owner)
122  return FileHandler(_file_name, mode)
123 
124 
125 @file_exc_decorator

◆ run()

def ody_migr_executor.run ( )
Main entrypoint for all the migration operations.

Definition at line 267 of file ody_migr_executor.py.

267 def run():
268  """Main entrypoint for all the migration operations."""
269  argv = sys.argv[1:]
270  args = get_parser().parse_args(argv)
271 
272  # verify that necessary env variables are exported
273  if os.getenv(ENV_MIGR_SECRET_KEY) is None:
274  if args.secret is None:
275  error_msg = ("SECRET KEY is required (either supply as --secret"
276  " argument or set `{}` env variable.)".format(
277  ENV_MIGR_SECRET_KEY))
278  LOGGER.error(error_msg)
279  raise SystemExit(error_msg)
280  os.environ[ENV_MIGR_SECRET_KEY] = args.secret.strip()
281 
282  if os.getenv(ENV_MIGR_SECRET_KEY) is None:
283  error_msg = "Environment variable: `{}` is not set!".format(
284  ENV_MIGR_SECRET_KEY)
285  LOGGER.error(error_msg)
286  raise SystemExit(error_msg)
287 
288  requested_cu = args.cu.strip()
289  # log formatter
290  log_formatter = Formatter('%(asctime)s '
291  '%(levelname)-4s [{}-{}-{}-{}] '
292  '(%(name)s|%(funcName)s:%(lineno)d) :: '
293  '%(message)s'
294  .format(args.server.strip().upper(),
295  args.data_category.strip().upper(),
296  args.action.strip().upper(),
297  requested_cu))
298 
299  # fill up LOG_FILE_NAME structure
300  log_file = LOG_FILE_NAME.format(
301  requested_cu.lower(),
302  args.action.strip().lower(),
303  args.data_category.strip().lower())
304 
305  # setup logging
306  setup_logging(args.verbose,
307  args.disable_logging,
308  log_formatter,
309  requested_cu,
310  args.server.strip(),
311  log_file)
312 
313  # log file exists (does not necessarily mean that requested_cu has been
314  # set up appropriatedly in Odyssey for migration)
315  LOGGER.info("MIGRATION STARTED: [Data Category: {}, "
316  "Operation: {}, CU: {}]".format(
317  args.data_category.strip().upper(),
318  args.action.strip().upper(),
319  requested_cu))
320 
321  # validate CU and servers for action=migrate only
322  if args.action.strip() == "migrate":
323  # Check if the requested_cu and servers are valid
324  if requested_cu in SUPPORTED_DEV_CU and args.server.strip() == "www4":
325  LOGGER.warning("Relaxing validation for DEV CUs. "
326  "Allowed DEV CUs are: {}".format(SUPPORTED_DEV_CU))
327  else:
328  allow_cu_migration = False
329  valid_cu_info = {}
330 
331  all_cu_list_monitor = get_valid_cu_list()
332 
333  for cu_info in all_cu_list_monitor:
334  # initially, we are only allowing "Live" CU's for migration
335  if cu_info['cutype'].strip() in ["Live"] and cu_info[
336  'user_name'].strip().upper() == requested_cu and cu_info[
337  'www_server'].strip() == args.server.strip():
338  allow_cu_migration = True
339  valid_cu_info = cu_info
340  break
341  else:
342  continue
343 
344  if(allow_cu_migration):
345  LOGGER.info("CUCODE `{}` [{}] is valid for "
346  "server `{}` in production."
347  .format(
348  valid_cu_info['user_name'].strip(),
349  valid_cu_info['cutype'].strip(),
350  valid_cu_info['www_server'].strip())
351  )
352  else:
353  error_msg = ("CUCODE `{}` is not valid for "
354  "server `{}` in production."
355  .format(requested_cu, args.server.strip()))
356  LOGGER.error(error_msg)
357  raise SystemExit(error_msg)
358 
359  # when running the scripts individually in the web-container,
360  # passwords are being displayed as raw text; this is just a
361  # simple attempt to mask the exact strings
362  args.password = base64.b64decode(args.password.strip()).decode()
363 
364  # prepare arguments for migration
365  args_migration = [
366  args.cu.strip(),
367  args.server.strip(),
368  args.action.strip(),
369  args.username.strip(),
370  args.password,
371  args.verbose
372  ]
373 
374  # adjust arguments for memdata and memhist
375  if args.data_category in [DATA_OPT_MEMHIST, DATA_OPT_MEMDATA,
376  DATA_OPT_SWITCH_ACCOUNTS]:
377  args_migration.append(args.data_category)
378  args_migration.append(args.reset_and_migrate)
379 
380  # adjust arguments for admin
381  elif args.data_category in [DATA_OPT_ADMIN, DATA_OPT_LOANAPP]:
382  args_migration.append(args.reset_and_migrate)
383 
384  # start migration
385  migration_exec_resp = MIGRATION_ENTRYPOINTS[args.data_category](*args_migration)
386 
387  LOGGER.info("MIGRATION DONE: [Data Category: {}, "
388  "Operation: {}, CU: {}]".format(
389  args.data_category.strip().upper(),
390  args.action.strip().upper(),
391  args.cu.strip()))
392  return migration_exec_resp
393 
394 

◆ setup_logging()

def ody_migr_executor.setup_logging (   _verbose,
  _disable_logging,
  formatter,
  _cu,
  _server,
  _log_file 
)
Setup logging configuration and handlers.

Definition at line 126 of file ody_migr_executor.py.

126 def setup_logging(_verbose,
127  _disable_logging,
128  formatter,
129  _cu,
130  _server,
131  _log_file):
132  """Setup logging configuration and handlers."""
133  log_file_dir = os.path.dirname(_log_file)
134 
135  # if log directory/file does not exist
136  if not os.path.exists(log_file_dir):
137  err_msg = ("Log directory `{}` does not exist. "
138  "MAKE SURE OF THE FOLLOWING: "
139  "(i) `/home/{}/*` directories are in place (perhaps, `{}` "
140  "is not created in Odyssey?). "
141  "(ii) CUCODE `{}` is a valid Mammoth target for migration "
142  "in `{}`."
143  "(iii) CUCODE `{}` is an intended Odyssey source "
144  "for migration.").format(
145  log_file_dir,
146  _cu.lower(),
147  _cu.upper(),
148  _cu.upper(),
149  _server,
150  _cu.upper())
151  raise Exception(err_msg)
152 
153  log_dir_ownership = get_ownership_detail(log_file_dir)
154 
155  fh = owned_file_handler(_log_file, mode="w", owner=log_dir_ownership)
156  fh.setFormatter(formatter)
157  LOGGER.addHandler(fh)
158  handlers = [fh]
159 
160  if(_disable_logging):
161  logging.basicConfig(level=logging.CRITICAL, handlers=handlers)
162 
163  else:
164  if _verbose:
165  logging.basicConfig(level=logging.DEBUG, handlers=handlers)
166  else:
167  logging.basicConfig(level=logging.INFO, handlers=handlers)
168 
169 

Variable Documentation

◆ MIGRATION_ENTRYPOINTS

dictionary ody_migr_executor.MIGRATION_ENTRYPOINTS
Initial value:
1 = {
2  DATA_OPT_SETTINGS: migrate_settings,
3  DATA_OPT_MEMDATA: migrate_members,
4  DATA_OPT_SWITCH_ACCOUNTS: migrate_members,
5  DATA_OPT_MEMHIST: migrate_members,
6  DATA_OPT_ADMIN: migrate_admin,
7  DATA_OPT_LOANAPP: migrate_loanapps
8 }

Definition at line 90 of file ody_migr_executor.py.