Пример #1
0
def shrink(self, pool, pool_ticket, issue, cowboy_mode=False):
  pool = Session.merge(pool)
  pool_ticket = Session.merge(pool_ticket)
  self.task = Session.merge(self.task)
  try:
    c_start = JiraApi.get_now()
    jira.start_crq(issue, log=self.log, cowboy_mode=cowboy_mode)
    for t in issue.fields.subtasks:
      t_start = JiraApi.get_now()
      t2 = jira.instance.issue(t.key)
      jira.start_task(t2, log=self.log, cowboy_mode=cowboy_mode)
      for a in t2.fields.attachment:
        pool_id, vm_id = a.filename.split('.', 2)[:2]
        member = PoolMembership.query.filter_by(pool=pool, vm_id=vm_id).first()
        member.retire()
        Session.delete(member)
        Session.commit()
        self.log.msg("Retired VM {} and removed it as member of pool {}".format(member.vm_id, pool.name))
      jira.complete_task(t, start_time=t_start, log=self.log, cowboy_mode=cowboy_mode)
    self.log.msg("waiting for 120 seconds before running post task diagnostics")
    time.sleep(120)
    run_diagnostics_on_pool(pool, self.log)
    jira.complete_crq(issue, start_time=c_start, log=self.log, cowboy_mode=cowboy_mode)
  except Exception as e:
    self.log.err("Error occured: {}".format(e))
    jira.cancel_crq_and_tasks(issue, comment="an exception occured running this change: {}".format(e))
    raise e
  finally:
    pool_ticket.done = True
    Session.merge(pool_ticket)
    Session.commit()
Пример #2
0
def update(self, pool, pool_ticket, issue, cowboy_mode=False):
    pool = Session.merge(pool)
    pool_ticket = Session.merge(pool_ticket)
    self.task = Session.merge(self.task)
    one_proxy = OneProxy(pool.cluster.zone.xmlrpc_uri,
                         pool.cluster.zone.session_string,
                         verify_certs=False)
    try:
        c_start = JiraApi.get_now()
        jira.start_crq(issue, log=self.log, cowboy_mode=cowboy_mode)
        for t in issue.fields.subtasks:
            t_start = JiraApi.get_now()
            t2 = jira.instance.issue(t.key)
            jira.start_task(t2, log=self.log, cowboy_mode=cowboy_mode)
            updated_members = []
            for a in t2.fields.attachment:
                pool_id, vm_id = a.filename.split('.', 2)[:2]
                template = a.get().decode(encoding="utf-8", errors="strict")
                member = PoolMembership.query.filter_by(pool=pool,
                                                        vm_id=vm_id).first()
                vm_name = member.vm_name
                member.retire()
                Session.delete(member)
                Session.commit()
                new_id = one_proxy.create_vm(template=template)
                new_member = PoolMembership(pool=pool,
                                            vm_name=vm_name,
                                            vm_id=new_id,
                                            template=template,
                                            date_added=datetime.utcnow())
                Session.add(new_member)
                Session.commit()
                self.log.msg(
                    "Instantiated new VM ID {} and added as member of pool {}".
                    format(new_member.vm_id, pool.name))
                updated_members.append(new_member)
            self.log.msg(
                "waiting for 120 seconds before running post change diagnostics"
            )
            time.sleep(120)
            run_diagnostics_on_pool(pool, self.log)
            jira.complete_task(t,
                               start_time=t_start,
                               log=self.log,
                               cowboy_mode=cowboy_mode)
        jira.complete_crq(issue,
                          start_time=c_start,
                          log=self.log,
                          cowboy_mode=cowboy_mode)
    except Exception as e:
        self.log.err("Error occured: {}".format(e))
        jira.cancel_crq_and_tasks(
            issue,
            comment="an exception occured running this change: {}".format(e))
        raise e
    finally:
        pool_ticket.done = True
        Session.merge(pool_ticket)
        Session.commit()
Пример #3
0
def diagnostic_worker(q, results, log):
  while True:
    work = q.get()
    member = Session.merge(work['member'])
    diagnostic = Diagnostic(user=app.config['SSH_HEALTH_CHECK_USER'],
                            host=member.vm_name,
                            ssh_identity_file=app.config['SSH_IDENTITY_FILE'],
                            cmd=app.config['SSH_HEALTH_CHECK_CMD'],
                            timeout=app.config['SSH_HEALTH_CHECK_TIMEOUT'])
    try:
      run_diagnostic(diagnostic)
      log.msg("diagnostic against {} succeeded".format(diagnostic.host))
    except Exception as e:
      log.err("diagnostic error: {}".format(e))
    finally:
      Session.add(PoolMemberDiagnostic(
        vm_id=member.vm_id,
        pool=member.pool,
        start_date=diagnostic.start_date,
        end_date=diagnostic.end_date,
        stdout=diagnostic.stdout,
        stderr=diagnostic.stderr,
        exitcode=diagnostic.exitcode))
      Session.commit()
      results.append(diagnostic)
      q.task_done()
Пример #4
0
def diagnostic_worker(q, results, log):
    while True:
        work = q.get()
        member = Session.merge(work['member'])
        diagnostic = Diagnostic(
            user=app.config['SSH_HEALTH_CHECK_USER'],
            host=member.vm_name,
            ssh_identity_file=app.config['SSH_IDENTITY_FILE'],
            cmd=app.config['SSH_HEALTH_CHECK_CMD'],
            timeout=app.config['SSH_HEALTH_CHECK_TIMEOUT'])
        try:
            run_diagnostic(diagnostic)
            log.msg("diagnostic against {} succeeded".format(diagnostic.host))
        except Exception as e:
            log.err("diagnostic error: {}".format(e))
        finally:
            Session.add(
                PoolMemberDiagnostic(vm_id=member.vm_id,
                                     pool=member.pool,
                                     start_date=diagnostic.start_date,
                                     end_date=diagnostic.end_date,
                                     stdout=diagnostic.stdout,
                                     stderr=diagnostic.stderr,
                                     exitcode=diagnostic.exitcode))
            Session.commit()
            results.append(diagnostic)
            q.task_done()
Пример #5
0
def worker(q, number):
  while True:
    work = q.get()
    member = Session.merge(work['member'])
    vm = work['vm']
    diagnostic = work['diagnostic']
    try:
      logging.info("[{}] about to run diagnostic on {}".format(number, diagnostic.host))
      diagnostic.run()
      logging.info("[{}] finished running diagnostic on {} (exit code: {})".format(number, vm.id, diagnostic.exitcode))
      Session.add(PoolMemberDiagnostic(
        vm_id=vm.id,
        pool=member.pool,
        start_date=diagnostic.start_date,
        end_date=diagnostic.end_date,
        stdout=diagnostic.stdout,
        stderr=diagnostic.stderr,
        exitcode=diagnostic.exitcode))
      Session.commit()
      logging.info("[{}] Saved diagnostic for {}".format(number, vm.id))
    except Exception as e:
      defect_ticket = jira.defect_for_exception(
        summary_title="Lifeguard: Health Check => {})".format(e),
        tb=traceback.format_exc(),
        e=e)
      logging.error("[{} worker experienced an error running diagnostic "
                    "host {}, error: {}, created defect ticket {}".format(
        number, vm.name, e, defect_ticket.key))
    finally:
      q.task_done()
Пример #6
0
def expand(self, pool, pool_ticket, issue, cowboy_mode=False):
  new_vm_ids = []
  pool = Session.merge(pool)
  pool_ticket = Session.merge(pool_ticket)
  self.task = Session.merge(self.task)
  one_proxy = OneProxy(pool.cluster.zone.xmlrpc_uri, pool.cluster.zone.session_string, verify_certs=False)
  try:
    c_start = JiraApi.get_now()
    jira.start_crq(issue, log=self.log, cowboy_mode=cowboy_mode)
    for t in issue.fields.subtasks:
      t_start = JiraApi.get_now()
      t2 = jira.instance.issue(t.key)
      jira.start_task(t2, log=self.log, cowboy_mode=cowboy_mode)
      for a in t2.fields.attachment:
        pool_id, vm_name = a.filename.split('.', 2)[:2]
        template = a.get().decode(encoding="utf-8", errors="strict")
        vm_id = one_proxy.create_vm(template=template)
        new_vm_ids.append(vm_id)
        m = PoolMembership(pool=pool, vm_name=vm_name, vm_id=vm_id, template=a.get(), date_added=datetime.utcnow())
        Session.merge(m)
        Session.commit()
        self.log.msg("created new vm: {}".format(vm_name))
      jira.complete_task(t, start_time=t_start, log=self.log, cowboy_mode=cowboy_mode)
    self.log.msg("waiting for 120 seconds before running post task diagnostics")
    time.sleep(120)
    run_diagnostics_on_pool(pool, self.log)
    jira.complete_crq(issue, start_time=c_start, log=self.log, cowboy_mode=cowboy_mode)
  except Exception as e:
    self.log.err("Error occured: {}".format(e))
    jira.cancel_crq_and_tasks(issue, comment="an exception occured running this change: {}".format(e))
    raise e
  finally:
    pool_ticket.done = True
    Session.merge(pool_ticket)
    Session.commit()
Пример #7
0
 def run_task(self, **kwargs):
     Session.merge(self.task)
     self.task.start_time = datetime.utcnow()
     self.task.ident = threading.get_ident()
     self.task.status = TaskStatus.running.value
     Session.merge(self.task)
     Session.commit()
     try:
         self.run_function(**kwargs)
         self.task.log = self.log.messages
         self.task.end_time = datetime.utcnow()
         self.task.status = TaskStatus.finished.value
         self.task.result = TaskResult.success.value
         self.task = Session.merge(self.task)
         Session.commit()
     except Exception as e:
         self.task.log = self.log.messages
         self.task.tb = traceback.format_exc()
         self.task.end_time = datetime.utcnow()
         self.task.status = TaskStatus.finished.value
         self.task.result = TaskResult.fail.value
         self.task = Session.merge(self.task)
         Session.commit()
         defect = jira.defect_for_exception(
             "Background Task Error: {}".format(self.task.name),
             e,
             tb=traceback.format_exc(),
             username=self.task.username)
         self.task.defect_ticket = defect.key
         self.task = Session.merge(self.task)
         Session.commit()
     finally:
         Session.remove()
Пример #8
0
 def run_task(self, **kwargs):
   Session.merge(self.task)
   self.task.start_time = datetime.utcnow()
   self.task.ident = threading.get_ident()
   self.task.status = TaskStatus.running.value
   Session.merge(self.task)
   Session.commit()
   try:
     self.run_function(**kwargs)
     self.task.log = self.log.messages
     self.task.end_time = datetime.utcnow()
     self.task.status = TaskStatus.finished.value
     self.task.result = TaskResult.success.value
     self.task = Session.merge(self.task)
     Session.commit()
   except Exception as e:
     self.task.log = self.log.messages
     self.task.tb = traceback.format_exc()
     self.task.end_time = datetime.utcnow()
     self.task.status = TaskStatus.finished.value
     self.task.result = TaskResult.fail.value
     self.task = Session.merge(self.task)
     Session.commit()
     defect = jira.defect_for_exception(
       "Background Task Error: {}".format(
         self.task.name),
       e, tb=traceback.format_exc(),
       username=self.task.username)
     self.task.defect_ticket = defect.key
     self.task = Session.merge(self.task)
     Session.commit()
   finally:
     Session.remove()
Пример #9
0
def shrink(self, pool, pool_ticket, issue, cowboy_mode=False):
    pool = Session.merge(pool)
    pool_ticket = Session.merge(pool_ticket)
    self.task = Session.merge(self.task)
    try:
        c_start = JiraApi.get_now()
        jira.start_crq(issue, log=self.log, cowboy_mode=cowboy_mode)
        for t in issue.fields.subtasks:
            t_start = JiraApi.get_now()
            t2 = jira.instance.issue(t.key)
            jira.start_task(t2, log=self.log, cowboy_mode=cowboy_mode)
            for a in t2.fields.attachment:
                pool_id, vm_id = a.filename.split('.', 2)[:2]
                member = PoolMembership.query.filter_by(pool=pool,
                                                        vm_id=vm_id).first()
                member.retire()
                Session.delete(member)
                Session.commit()
                self.log.msg(
                    "Retired VM {} and removed it as member of pool {}".format(
                        member.vm_id, pool.name))
            jira.complete_task(t,
                               start_time=t_start,
                               log=self.log,
                               cowboy_mode=cowboy_mode)
        self.log.msg(
            "waiting for 120 seconds before running post task diagnostics")
        time.sleep(120)
        run_diagnostics_on_pool(pool, self.log)
        jira.complete_crq(issue,
                          start_time=c_start,
                          log=self.log,
                          cowboy_mode=cowboy_mode)
    except Exception as e:
        self.log.err("Error occured: {}".format(e))
        jira.cancel_crq_and_tasks(
            issue,
            comment="an exception occured running this change: {}".format(e))
        raise e
    finally:
        pool_ticket.done = True
        Session.merge(pool_ticket)
        Session.commit()
Пример #10
0
 def __init__(self, task, run_function, log=None, **kwargs):
   if None in [task, run_function]:
     raise Exception("Required parameter(s) is None: task={}, run_function={}".format(task, run_function))
   if log is not None:
     self.log = log
   else:
     self.log = DumbLog()
   self.task = None
   self.task = Session.merge(task)
   self.run_function = types.MethodType(run_function, self)
   super().__init__(target=self.run_task, daemon=True, kwargs=kwargs)
Пример #11
0
 def __init__(self, task, run_function, log=None, **kwargs):
     if None in [task, run_function]:
         raise Exception(
             "Required parameter(s) is None: task={}, run_function={}".
             format(task, run_function))
     if log is not None:
         self.log = log
     else:
         self.log = DumbLog()
     self.task = None
     self.task = Session.merge(task)
     self.run_function = types.MethodType(run_function, self)
     super().__init__(target=self.run_task, daemon=True, kwargs=kwargs)
Пример #12
0
def create_update_ticket(name, pool, members, update_members):
    logging.info("[{}] {} requires updating and an existing change ticket doesn't exist".format(name, pool.name))
    title = 'Plan Update => Pool {} ({}/{} members need updates)'.format(pool.name, len(members), pool.cardinality)
    description = "Pool update triggered that will update {} VM(s): \n\n*{}".format(
          len(update_members),
          "\n*".join([m.vm.name for m in update_members]))
    task = Task(
      name=title,
      description="{}\n{}".format(title, description),
      username="******")
    Session.add(task)
    Session.commit()
    task_thread = TaskThread(task=task,
                             run_function=plan_update,
                             pool=Session.merge(pool),
                             update_members=update_members)
    threads.append(task_thread)
    logging.info("[{}] launched background task {} to {}".format(name, task.id, title))
Пример #13
0
def create_expansion_ticket(name, pool, members, expansion_names):
  logging.info("[{}] {} requires expansion and an existing change ticket doesn't exist".format(name, pool.name))
  title = 'Plan Change => Pool Expansion: {} ({} members to {})'.format(pool.name, len(members), pool.cardinality)
  description = "Pool expansion triggered that will instantiate {} new VM(s): \n\n*{}".format(
        len(expansion_names),
        "\n*".join(expansion_names))
  task = Task(
    name=title,
    description="{}\n{}".format(title, description),
    username="******")
  Session.add(task)
  Session.commit()
  task_thread = TaskThread(task=task,
                           run_function=plan_expansion,
                           pool=Session.merge(pool),
                           expansion_names=expansion_names)
  threads.append(task_thread)
  logging.info("[{}] launched background task {} to {}".format(name, task.id, title))
Пример #14
0
def worker(name, q, cowboy_mode, plan, implement):
  while True:
    work = q.get()
    members = work['members']
    pool = Session.merge(work['pool'])
    if cowboy_mode:
      name = "{}_{}".format('cowboy', name)
      logging.info("[{}] called in cowbow mode.  All change management "
                   "protocol around status/schedule will be ingored".format(name))
    try:
      logging.info("[{}] assigned tickets for pool {}".format(name, pool.name))
      if plan:
        existing_ticket = pool.pending_ticket()
        expansion_names = pool.get_expansion_names(members)
        shrink_members = pool.get_members_to_shrink(members)
        update_members = pool.get_update_members(members)
        if existing_ticket is not None:
          logging.info("[{}] existing expand ticket {} already created for {}".format(
            name, existing_ticket.ticket_key, pool.name))
        elif expansion_names:
          create_expansion_ticket(name, pool, members, expansion_names)
        elif shrink_members:
          create_shrink_ticket(name, pool, members, shrink_members)
        elif update_members:
          create_update_ticket(name, pool, members, update_members)
      else:
        logging.info("[{}] skipping ticket planning".format(name))
      if implement:
        execute_tickets(name, pool, cowboy_mode)
      else:
        logging.info("[{}] skipping ticket implementation".format(name))
      logging.info("[{}] finished working on tickets for pool {}".format(name, pool.name))
    except Exception as e:
      defect_ticket = jira.defect_for_exception(
        username="******",
        summary_title="Lifeguard: Health Check => {})".format(e),
        tb=traceback.format_exc(),
        e=e)
      logging.error("[{}] experienced an error pool {}, error: {}, created defect ticket {}".format(
        name, pool.name, e, defect_ticket.key))
    finally:
      q.task_done()
Пример #15
0
def plan_update(self, pool, update_members):
    task = crq = None
    try:
        pool = Session.merge(pool)
        start, end = jira.next_immediate_window_dates()
        logging = jira.instance.issue('SVC-1020')
        crq = jira.instance.create_issue(
            project=app.config['JIRA_CRQ_PROJECT'],
            issuetype={'name': 'Change Request'},
            assignee={'name': app.config['JIRA_USERNAME']},
            summary="[IMPLEMENT] {}".format(self.task.name),
            description=self.task.description,
            customfield_14530=start,
            customfield_14531=end,
            customfield_19031={'value': 'Maintenance'},
            customfield_15152=[{
                'value': 'Global'
            }],
            customfield_19430={'value': 'No conflict with any restrictions'},
            customfield_14135={
                'value': 'IPG',
                'child': {
                    'value': 'IPG Big Data'
                }
            },
            customfield_17679="Pool update required")
        self.log.msg("Created change request: {}".format(crq.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNING'])
        self.log.msg("Transitioned {} to planning".format(crq.key))
        jira.instance.create_issue_link('Relate', crq, logging)
        self.log.msg("Related {} to LOGGING service {}".format(
            crq.key, logging.key))
        batch_size = floor(
            len(update_members) / app.config['BATCH_SIZE_PERCENT'])
        batch_size = 1 if batch_size == 0 else batch_size
        num_batches = min(len(update_members),
                          app.config['BATCH_SIZE_PERCENT'])
        self.log.msg(
            "updating {} hosts in pool {} requires {} tasks with no more than {} hosts per task"
            .format(len(update_members), pool.name, num_batches, batch_size))
        batch_num = 0
        while len(update_members):
            batch_num += 1
            task = jira.instance.create_issue(
                issuetype={'name': 'MOP Task'},
                assignee={'name': app.config['JIRA_USERNAME']},
                project=app.config['JIRA_CRQ_PROJECT'],
                summary='[TASK {}/{} (Update {}%)] {}'.format(
                    batch_num, num_batches, app.config['BATCH_SIZE_PERCENT'],
                    self.task.name),
                parent={'key': crq.key},
                customfield_14135={
                    'value': 'IPG',
                    'child': {
                        'value': 'IPG Big Data'
                    }
                },
                customfield_15150={'value': 'No'})
            self.log.msg("Created task: {}".format(task.key))
            jira.instance.transition_issue(
                task, app.config['JIRA_TRANSITION_TASK_PLANNING'])
            self.log.msg("Transitioned {} to planning".format(task.key))
            env = Environment(loader=ObjectLoader())
            for num in range(0, min(len(update_members), batch_size)):
                m = update_members.pop()
                filename = '{}.{}.template'.format(m.pool.id, m.vm_id)
                attachment_content = io.StringIO(m.current_template())
                jira.instance.add_attachment(issue=task,
                                             filename=filename,
                                             attachment=attachment_content)
                self.log.msg("Attached template for {} to task {}".format(
                    filename, task.key))
            jira.instance.transition_issue(
                task, app.config['JIRA_TRANSITION_TASK_WRITTEN'])
            self.log.msg("Transitioned task {} to written".format(task.key))
            jira.approver_instance.transition_issue(
                task, app.config['JIRA_TRANSITION_TASK_APPROVED'])
            self.log.msg("Approved task {}".format(task.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNED_CHANGE'])
        self.log.msg("Transitioned change request {} to approved".format(
            task.key))
        jira.approver_instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_APPROVED'])
        self.log.msg("Transitioned change request {} to approved".format(
            crq.key))
        self.log.msg("Task ID {}".format(self.task.id))
        db_ticket = PoolTicket(pool=pool,
                               action_id=PoolTicketActions.update.value,
                               ticket_key=crq.key,
                               task=Session.merge(self.task))
        Session.add(db_ticket)
        Session.commit()
    except Exception as e:
        Session.rollback()
        if crq is not None:
            jira.cancel_crq_and_tasks(
                crq, comment="failure creating change tickets")
        raise e
Пример #16
0
def plan_shrink(self, pool, shrink_members):
    task = crq = None
    try:
        pool = Session.merge(pool)
        start, end = jira.next_immediate_window_dates()
        logging = jira.instance.issue('SVC-1020')
        crq = jira.instance.create_issue(
            project=app.config['JIRA_CRQ_PROJECT'],
            issuetype={'name': 'Change Request'},
            assignee={'name': app.config['JIRA_USERNAME']},
            summary="[IMPLEMENT] {}".format(self.task.name),
            description=self.task.description,
            customfield_14530=start,
            customfield_14531=end,
            customfield_19031={'value': 'Maintenance'},
            customfield_15152=[{
                'value': 'Global'
            }],
            customfield_19430={'value': 'No conflict with any restrictions'},
            customfield_14135={
                'value': 'IPG',
                'child': {
                    'value': 'IPG Big Data'
                }
            },
            customfield_17679="Pool shrink required")
        self.log.msg("Created change request: {}".format(crq.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNING'])
        self.log.msg("Transitioned {} to planning".format(crq.key))
        jira.instance.create_issue_link('Relate', crq, logging)
        self.log.msg("Related {} to LOGGING service {}".format(
            crq.key, logging.key))
        task = jira.instance.create_issue(
            issuetype={'name': 'MOP Task'},
            assignee={'name': app.config['JIRA_USERNAME']},
            project=app.config['JIRA_CRQ_PROJECT'],
            summary='[IMPLEMENTATION TASK] {}'.format(self.task.name),
            parent={'key': crq.key},
            customfield_14135={
                'value': 'IPG',
                'child': {
                    'value': 'IPG Big Data'
                }
            },
            customfield_15150={'value': 'No'})
        self.log.msg("Created task: {}".format(task.key))
        jira.instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_PLANNING'])
        self.log.msg("Transitioned {} to planning".format(task.key))
        for m in [Session.merge(m) for m in shrink_members]:
            filename = '{}.{}.template'.format(pool.id, m.vm_id)
            attachment_content = io.StringIO(m.template)
            jira.instance.add_attachment(issue=task,
                                         filename=filename,
                                         attachment=attachment_content)
            self.log.msg("Attached member {} to shrink to task {}".format(
                filename, task.key))
        jira.instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_WRITTEN'])
        self.log.msg("Transitioned task {} to written".format(task.key))
        jira.approver_instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_APPROVED'])
        self.log.msg("Approved task {}".format(task.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNED_CHANGE'])
        self.log.msg("Transitioned task {} to approved".format(task.key))
        jira.approver_instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_APPROVED'])
        self.log.msg("Transitioned change request {} to approved".format(
            crq.key))
        self.log.msg("Task ID {}".format(self.task.id))
        db_ticket = PoolTicket(pool=pool,
                               action_id=PoolTicketActions.shrink.value,
                               ticket_key=crq.key,
                               task=Session.merge(self.task))
        Session.add(db_ticket)
        Session.commit()
    except Exception as e:
        if crq is not None:
            jira.cancel_crq_and_tasks(
                crq, comment="failure creating change tickets")
        raise e
Пример #17
0
def plan_expansion(self, pool, expansion_names):
    task = crq = None
    try:
        pool = Session.merge(pool)
        start, end = jira.next_immediate_window_dates()
        logging = jira.instance.issue('SVC-1020')
        crq = jira.instance.create_issue(
            project=app.config['JIRA_CRQ_PROJECT'],
            issuetype={'name': 'Change Request'},
            assignee={'name': app.config['JIRA_USERNAME']},
            summary='[TEST IMPLEMENT] {}'.format(self.task.name),
            description=self.task.description,
            customfield_14530=start,
            customfield_14531=end,
            customfield_19031={'value': 'Maintenance'},
            customfield_15152=[{
                'value': 'Global'
            }],
            customfield_19430={'value': 'No conflict with any restrictions'},
            customfield_14135={
                'value': 'IPG',
                'child': {
                    'value': 'IPG Big Data'
                }
            },
            customfield_17679="Pool expansion required")
        self.log.msg("Created change request: {}".format(crq.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNING'])
        self.log.msg("Transitioned {} to planning".format(crq.key))
        jira.instance.create_issue_link('Relate', crq, logging)
        self.log.msg("Related {} to LOGGING service {}".format(
            crq.key, logging.key))
        task = jira.instance.create_issue(
            issuetype={'name': 'MOP Task'},
            assignee={'name': app.config['JIRA_USERNAME']},
            project=app.config['JIRA_CRQ_PROJECT'],
            description=
            "Instanitate the attached templates in the zone associated "
            "to the pool identified in the filename <pool_id>.<hostname>",
            summary='[TEST IMPLEMENTATION TASK] {}'.format(self.task.name),
            parent={'key': crq.key},
            customfield_14135={
                'value': 'IPG',
                'child': {
                    'value': 'IPG Big Data'
                }
            },
            customfield_15150={'value': 'No'})
        self.log.msg("Created task: {}".format(task.key))
        jira.instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_PLANNING'])
        self.log.msg("Transitioned {} to planning".format(task.key))
        env = Environment(loader=ObjectLoader())
        for hostname in expansion_names:
            vars = VarParser.parse_kv_strings_to_dict(
                pool.cluster.zone.vars, pool.cluster.vars, pool.vars,
                'hostname={}'.format(hostname))
            vm_template = env.from_string(pool.template).render(pool=pool,
                                                                vars=vars)
            attachment_content = io.StringIO(vm_template)
            jira.instance.add_attachment(issue=task,
                                         filename='{}.{}.template'.format(
                                             pool.id, hostname),
                                         attachment=attachment_content)
            self.log.msg("Attached template for {} to task {}".format(
                hostname, task.key))
        jira.instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_WRITTEN'])
        self.log.msg("Transitioned task {} to written".format(task.key))
        jira.approver_instance.transition_issue(
            task, app.config['JIRA_TRANSITION_TASK_APPROVED'])
        self.log.msg("Approved task {}".format(task.key))
        jira.instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_PLANNED_CHANGE'])
        self.log.msg("Transitioned task {} to approved".format(task.key))
        jira.approver_instance.transition_issue(
            crq, app.config['JIRA_TRANSITION_CRQ_APPROVED'])
        self.log.msg("Transitioned change request {} to approved".format(
            crq.key))
        self.log.msg("Task ID {}".format(self.task.id))
        db_ticket = PoolTicket(pool=Session.merge(pool),
                               action_id=PoolTicketActions.expand.value,
                               ticket_key=crq.key,
                               task=Session.merge(self.task))
        Session.add(db_ticket)
        Session.commit()
    except Exception as e:
        Session.rollback()
        if crq is not None:
            jira.cancel_crq_and_tasks(
                crq, comment="failure creating change tickets")
        raise e