#!/usr/bin/python
#------------------------------------------------------------------
# install -
#
# Copyright (c) 2015-2022 by cisco Systems, Inc.
# All rights reserved.
#------------------------------------------------------------------
#

import sys
sys.dont_write_bytecode = True
import os
import commands
import re
from HTMLParser import HTMLParser
import ftplib
import optparse
import getpass
import package
import shutil
import subprocess
import traceback
import logging 
import logging.handlers
import rpmutils
from urlparse import urlparse
from urlparse import urlunparse
from sdr_instadd import collect_vms_info
import copy
import time
import fcntl 
import signal
import functools 
import inst_config
import chkpt
import json
import tempfile
from xr_util import *
     
if 'actioptim' not in sys.argv and 'prepoptim' not in sys.argv:
    print "+" * 80
MAX_CLI_LEN = 1020
TIMEOUT_YESNO = 120
ADMIN_ACCESS_CHK_TIMEOUT=30
VM_INFO_FILE = "/tmp/vm_info.json"
UPDATE_STATUS_FILE_XR = "/tmp/status_update.txt"
UPDATE_STATUS_FILE_CALV = "/tmp/status_update.json"
USER_ABORT_FILE_ADD = "/tmp/abort_orch_add.txt"
USER_ABORT_FILE_PREP = "/tmp/abort_orch_prep.txt"
ALL_NODES = "all"
INCR = "increment"
DECR = "decrement"

new_oid = 1
add_oid = 1
sdr_actioncmd_error = "Error in executing cmd in Sysadmin"
orch_success = "Orchestration done"

oper_bg_re = re.compile(r'Install operation will continue in the background')
oper_none_re = re.compile(r'No install operation in progress')
oper_id_re = re.compile(r'Install operation (?P<oper_id>\d+) started')
instmgr_impact_re = re.compile(r'Instmgr process needs to be restarted')
oper_reboot_re = re.compile(r'The software will be activated with system reboot')
oper_reload_re = re.compile(r'The software will be activated with reload upgrade')
oper_success_re = re.compile(r'Install operation \d+ finished successfully')
oper_oip_re = re.compile(r'There is already an install operation in progress on Admin')
oper_nodestatus_re = re.compile(r'Please ensure all nodes are Operational and try again')
no_admin_inactive_pkgs_re = re.compile(r'Inactive Packages: 0')
no_xr_inactive_pkgs_re    = re.compile(r'No inactive package')
no_space_left = re.compile(r'No space left on device')
http_nospace_left = 'curl: (23) Failed writing body'

'''
#===============================================================================
#Valid Syntax :
"install update source tftp://10.0.2.2:/ws [package list]"
   # The dependent SMU's will be pulled in for update
   # If no package list provided, all SMU in /ws which are not active on the
   # router will be added and installed
   # Only packages matching to running version will be downloaded
   # This CLI can be used to install optional packages by specifying explicit
   # package names.

"install upgrade source tftp://10.0.2.2:/ws version <VER> [package list]"
   # If no package list provided, all packages and SMU in /ws of version <VER>
   # will be added and installed

"upgrade" ==> base(mini) version will be upgraded.
              --> Without specified package list all installed packages are
                  upgraded to v2 version with latest SMUs in repo
              --> If packages are specified only given packages are upgraded
                  along with v2 mini to new version.
"update"  ==> Packages and SMUs other than base (mini) can be upgraded.
              --> Without specified packages , only SMUs for installed package
                  updated.
              --> If packages are specified with version , those packages will
                  upgraded.
              --> If packages are specified without version, only SMUs  for
                  specified packages are installed
syntax for repo : sftp://username@10.105.227.174/path
#===============================================================================
'''

TMP_ROOT = "/misc"
TMP_PATH = "disk1"
TMP_DIR = "install_tmp_staging_area"
REPO_PROTO = ['tftp', 'ftp', 'sftp', 'https', 'http', 'scp','harddisk','disk0','/']
OUTPUT_FILE = "/misc/disk1/calv_info.txt"
UPGRADE = 'upgrade'
UPDATE = 'update'
TIMEOUT = 30
TIMEOUT_YESNO = 120
TIMEOUT_USERNAME_PWD = 120
INSTALL_DB_DIR = '/var/log/install/instdb/'
INSTALL_DB_LOG_DIR = '/var/log/install/instdb/log/'
EXEC_TIMEOUT_CTL = "/pkg/bin/exec_timeout_ctl"
# The following iso naming convention is supported:
# <plat>-x.iso-version or <plat>-x-version.iso for x in ISO_NAME_TYPES
ISO_NAME_TYPES = ['-mini-x', '-fullk9-vmdk-x', '-full-vmdk-x', '-minik9-x', '-minik9-x64', 
                  '-fullk9-x', '-full-ova-x', '-mini-vmdk-x', '-fullk9-ova-x', 
                  '-mini-ova-x', '-full-x', '-mini-x64', '-full-x64', '-fullk9-x64', 
                  '-goldenk9-x', '-golden-x', '-golden-x64', '-goldenk9-x64']

'''
    Files created as part of optim workflow. Remove them upon abort.
'''
CFGFILE = "/root/config_system.cfg"
BRIDGE_SMU_INSTALL_MARKER_FILE  = "/var/log/install/instdb/brs_install_marker_file.txt"
PARENT_ID_FILE = "/var/log/install/instdb/parent_id.txt"

PREPCHKPTFILE = os.path.join (INSTALL_DB_DIR, chkpt.xr_prep_chkpt_file)
GISOCFGFILE = os.path.join (INSTALL_DB_DIR, 'router.cfg')
rem_on_abort = [CFGFILE, PREPCHKPTFILE, GISOCFGFILE, UPDATE_STATUS_FILE_XR, UPDATE_STATUS_FILE_CALV, VM_INFO_FILE, BRIDGE_SMU_INSTALL_MARKER_FILE]
rem_on_sucess = [CFGFILE, UPDATE_STATUS_FILE_XR, UPDATE_STATUS_FILE_CALV, VM_INFO_FILE]
exitworkflow_remfiles = [CFGFILE, VM_INFO_FILE]

# Golden ISO names 
GOLDEN_ISO_NAMES = ['-golden-x', '-goldenk9-x', '-fullk9-x', '-full-x', '-golden-x64', 
                    '-goldenk9-x64', '-fullk9-x64', '-full-x64',]

INSTALL_MARKER_FILE  = "/var/log/install/instdb/install_marker_file.txt"
RELOAD_PENDING_MARKER_FILE = "/tmp/su_abort_reload_pending"

UPGRADE_ALLOWED_MSG = "Upgrade is allowed"
UPGRADE_BLOCKED_MSG = "Upgrade is blocked"
FORCE_UPGRADE_NOT_SUPPORTED_MSG = "This upgrade is not supported even with the \'force\' option"
UPGRADE_NOT_SUPPORTED_MSG = "Upgrade is not supported"
COMPAT_MATRIX_DIR = "/pkg/bin/upgrade_matrix"
UPGRADE_MATRIX_LOGFILE = "/install/tmp/show_upgrade_compat_log"
STAGING_COMPAT_MATRIX_DIR = "/install/tmp/upgrade_matrix"
V1_STUB_COMPAT_MATRIX_FILE = "/misc/disk1/v1_stub_compatibility_matrix.json"
V2_STUB_COMPAT_MATRIX_FILE = "/misc/disk1/v2_stub_compatibility_matrix.json"
SKIP_MATRIX_CHECKS = "/tmp/skip_matrix_checks"
MATRIX_UNSUPPORTED_PLATFORMS = ['xrv9k', 'iosxrwbd']
INST_MIRROR_PATH = "/harddisk:/mirror"

#Pre and post check metric states and actions configured
METRIC_STATE = 0
METRIC_ACTION = 1
UNKNOWN = "0"
ENABLED = "1"
DISABLED = "2"
ERROR_ON_FAILURE = "0"
WARN_ON_FAILURE = "1"
REVERT_ON_FAILURE = "2"

#List of healthcheck tool metrics install is concerned with
HC_TOOL_METRICS = ['config-inconsistency', 'cpu', 'communication-timeout', 'filesystem', 'free-mem', 'shared-memory', 'platform', 'fpd', 'hw-monitoring', 'lc-monitoring', 'pci-monitoring', 'wd-monitoring', 'process-resource', 'process-status', 'system-clock']
PRE_POSTCHECKS_SUPPORTED_PLATFORMS = ['ncs1004']

global orig_golden_iso_name
orig_golden_iso_name = None 
changed_golden_iso_name = None 
is_new_label_format = False
golden_iso_label_name = None
global xractive_pkgs
xractive_pkgs = None
global active_sp 
active_sp = None
global act_cmd_op
act_cmd_op = None
global inact_cmd_op
inact_cmd_op = None
global platN
platN = None
global post_precheck_opid
post_precheck_opid = 1
global inst_replace_op_id
inst_replace_op_id = 0
global inst_replace_url_str 
inst_replace_url_str = None

global precheck_dict
precheck_dict = {}

try :
    import netns
    is_tpnns = True
    netns_cmd = 'ip netns exec global-vrf '
except:
    is_tpnns = False
    netns_cmd = ''

class ExitWorkflow (Exception):
    pass

class BridgeSMUInstallError (Exception):
    pass


class ListDirHTML(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.package_list = []

    def handle_starttag(self, tag, attrs):
        for attr in attrs:
            if attr[0] == 'href' and '.rpm' in attr[1]:
                self.package_list.append(attr[1])
            elif attr[0] == 'href' and '.pkg' in attr[1]:
                self.package_list.append(attr[1])
            elif attr[0] == 'href' and '.iso' in attr[1]:
                self.package_list.append(attr[1])
            elif attr[0] == 'href' and '.smu' in attr[1]:
                self.package_list.append(attr[1])

def prompt_timeout(signo, frame):
    logger.info("\nTimed-out while waiting for user response. Aborting the operation.\n")
    handle_abort_optim_workflow ()
    sys.exit(0)

def admin_access_check_timeout(signo, frame):
    logger.info("\nTimed-out checking access to sysadmin. Aborting the operation.\n")
    sys.exit(0)

class PkgDownload():
    def __init__(self, options, v1, dest, v2=None):
        self.options = options
        self.repo = options.repository
        self.dest = dest
        self.v1 = v1
        self.v2 = v2
        self.scheme = None
        self.hostname = None
        self.username = None
        self.password = None
        self.srcdir = None
        self.repopkgs = []
        self.downloaded_pkgs = []
        self.pkgstate = None
        self.messaged = False
        self.redownload = [] # This is temp untill install add is fixed
        self.__init_repo_info__(self.repo)
        self.clear_staging()

        scheme = self.repo.split(":")[0]
        if self.repo[0] == '/' or scheme == 'harddisk' or scheme == 'disk0':
            self.get = self.localget
            self.list = self.locallistdir

        if self.repo.split(":")[0] == 'tftp':
            self.get = self.tftpget
            self.list = self.tftplistdir

        elif self.repo.split(":")[0] == 'scp':
            self.get = self.scpget
            # This intentional SFTP and SCP have same method to listdir
            if options.replace:
                self.list = self.filednld
            else: 
                self.list = self.sftplistdir

        elif self.repo.split(":")[0] == 'sftp':
            self.get = self.sftpget
            if options.replace:
                self.list = self.filednld
            else: 
                self.list = self.sftplistdir

        elif self.repo.split(":")[0] == 'ftp':
            self.ftp = self.ftpconnect()
            self.get = self.ftpget
            if options.replace:
                self.list = self.filednld
            else:
                self.list = self.ftplistdir
            self.quit = self.ftpdisconnect

        elif self.repo.split(":")[0] == 'http':
            self.get = self.httpget
            if options.replace:
                ''' In case of replace, only a giso is input pkg '''
                ''' so disabling directory listing for it.       ''' 
                self.list = self.filednld
            else: 		
                self.list = self.httplistdir

        elif self.repo.split(":")[0] == 'https':
            self.get = self.httpget
            if options.replace:
                self.list = self.filednld
            else: 		
                self.list = self.httplistdir

    def __enter__(self):
        return self

    def clear_staging(self):
        for (root, dirs, files) in os.walk(self.dest):
            if os.path.ismount (root):
                cmd = "umount -R %s"%(root)
                run_cmd (cmd)
        if os.path.exists(self.dest):
            shutil.rmtree(self.dest)

    def __exit__(self,type,value,tb):
        self.clear_staging()

    def __init_repo_info__(self, repo):
        if repo[0] == '/' or repo[:9] == 'harddisk:' or repo[:9] == 'disk0:':
            if repo == 'harddisk:':
                self.srcdir = repo+'/'
            else :
                self.srcdir = repo
            self.scheme = "localdisk"
        else :
            repo = repo.replace(":", "", 1)  # Replace first instance of ":"
            repo = repo.replace("//", "/")  # Replace any double "/" in URL
            repo = repo.split("/")  # Each item in URL is sapreted
            self.scheme = repo[0]
            self.srcdir = "/".join(repo[2:])  # 2nd+ is path
        self.password = None

        # If username in URL
        if '@' in repo[1]:
            if repo[1].count('@') > 1:
                logger.error("More than one '@' char is present in src URL, please check the src URL.\n"
                             "Note: '@', ':' and '/' are delimiters in URL hence not allowed in password while giving password in src URL.\n"
                             "If these special characters are in password, please use \"password:\" prompt instead of giving in URL.\n")
                exit_with_errors()
            self.username, self.hostname = repo[1].split('@')
            if not self.hostname:
                logger.error("Host could not be extracted from URL, please check the src URL.\n"
                             "Note: '@', ':' and '/' are delimiters in URL hence not allowed in password while giving password in src URL.\n"
                             "If these special characters are in password, please use \"password:\" prompt instead of giving in URL.\n")
                exit_with_errors()
            if ":" in self.username:
                if self.username.count(':') > 1:
                    logger.error("More than one ':' char is present in src URL, please check the src URL.\n"
                                 "Note: '@', ':' and '/' are delimiters in URL hence not allowed in password while giving password in src URL.\n"
                                 "If these special characters are in password, please use \"password:\" prompt instead of giving in URL.\n")
                    exit_with_errors()
		self.username, self.password = self.username.split(':')
        else:
            self.hostname = repo[1]

        if not (repo[0] == '/' or self.scheme == 'disk0' or self.scheme == \
            'harddisk' ):
            assert (self.hostname), "HostName or IP address is required"
        if not self.scheme == 'http' and not self.scheme == 'https' \
            and not self.scheme == 'localdisk':
            if not self.username and self.scheme != 'tftp':
                signal.alarm(TIMEOUT_USERNAME_PWD)
                self.username = raw_input("Enter username for %s:" % (self.hostname))
                signal.alarm(0)
            if not self.password and self.scheme != 'tftp':
                t = "Enter password for %s@%s:" % (self.username, self.hostname)
                signal.alarm(TIMEOUT_USERNAME_PWD)
                self.password = getpass.getpass(t)
                signal.alarm(0)
        logger.info("Scheme : %s"% (self.scheme))
        if self.scheme == 'localdisk' :
            logger.info("Hostname : localhost")
        else :
            logger.info("Hostname : %s"% (self.hostname))
        if not self.scheme == 'http' and not self.scheme == 'https' :
            logger.info("Username : %s"% (self.username))
            logger.info("SourceDir : %s"%(self.srcdir))
        return 0

    def validate_ip(self, s):
        """This function returns True if no transaltion required, else false"""
        """Translation is not required for ipv4 addr and ping'able host"""
        cmd_str = netns_cmd + "/bin/ping -c 5 %s" %(s)
        ret, output = commands.getstatusoutput(cmd_str)
        lines = output.splitlines()
        for line in lines:
            if "64 bytes from" in line:
                return True
        expression = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
        if expression.match(s):
            return True
        return False

    def nslookup(self, host):
        """does an equivalent of nslookup using ping's output"""
        cmd_str = "ping %s" %(host)
        ret, output = commands.getstatusoutput(cmd_str)
        lines = output.splitlines()
        ip_addr = host
        for line in lines:
            if "Sending" in line:
                ipPattern = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
                ip_addr = re.findall(ipPattern, line)[0]
                break
        return ip_addr


    def tftpget(self, packages):
        if not packages:
            logger.error("TFTP does not supoort wildcard, package name required")
            exit_with_errors()

        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        if type(packages) != type([]):
            packages = packages.split(" ")

        for pkg in packages:

            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)

            df = os.path.basename(pkg.filename)
            src_f = pkg.filename.replace("//", "/")
            src_path = os.path.join(self.srcdir,src_f)
            dest_path = os.path.join(self.dest, df)

            if self.validate_ip(self.hostname):
                ip_addr = self.hostname
            else:
                ip_addr = self.nslookup(self.hostname)
            cmd = netns_cmd + "tftp -4 -m binary %s -c get %s %s"%(ip_addr,"/" + src_path,os.path.join(self.dest, df))
            download_pkgs(cmd, src_path, dest_path, src_f, self.redownload, self.hostname)
            
    def FtpDownload(self, block):
        """ Handle for ftp download """
        self.__fp__.write(block)

    def ftpget(self, pkgs):
        FTPBUFFER = 1024 * 1024
        # Do ftp disconnect, connect and list.
        # Ftp automatically detects a timeout and logs out the user
        # causing list to fail.
        self.quit()
        self.ftp = self.ftpconnect()
        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        self.__downloaded = 0
        for pkg in pkgs:
            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)
            filename = os.path.join("/", self.srcdir, pkg.filename)
            destfile = os.path.join(self.dest, pkg.filename)
            MAX_RETRY_COUNT = 2
            retry_count = 0 
            success_msg = "226 Transfer complete."
            ret_msg = None 
            while retry_count < MAX_RETRY_COUNT:
                retry_count = retry_count + 1  
                try :
                    self.__fp__ = open(destfile, 'wb')
                except :
                    logger.error("Failed to open %s"%(destfile))
                    exit_with_errors()
                try:
                    if pkg.filename not in self.redownload and retry_count == 1:
                        # dont delete below logging. Used for yang log parsing
                        logger.info("Fetching .... %s" % (pkg.filename))
                    if retry_count > 1:
                        logger.info("Retrying .... %s" %pkg.filename)
                    if is_tpnns:
                        with netns.NetNS(nsname='global-vrf'):
                            ret_msg = self.ftp.retrbinary('RETR ' + filename, self.FtpDownload, FTPBUFFER)
                    else :
                        ret_msg = self.ftp.retrbinary('RETR ' + filename, self.FtpDownload, FTPBUFFER)
                    if (ret_msg == success_msg):
                        valid_md5sum = validate_downloaded_package_md5sum(destfile)
                        logger.debug("md5sum of %s is %d" %(pkg.filename, valid_md5sum))  
                        if not valid_md5sum:
                            logger.info("md5sum of %s not matched" %pkg.filename)
                            if retry_count < MAX_RETRY_COUNT:
                                os.remove(destfile)
                                self.__fp__.close()
                                continue
                            logger.error("Failed while downloading from FTP server")
                            self.__fp__.close()
                            dir_path = os.path.dirname(os.path.realpath(destfile)) 
                            shutil.rmtree(dir_path)
                            exit_with_errors()
                        else:
                            break 
                except :
                    exc_info = sys.exc_info()
                    TB = traceback.format_exc()
                    logger.debug(TB)
                    logger.error("Failed while downloading from FTP server")
                    exit_with_errors()
            self.__fp__.close()
        return 0

    def ftpdisconnect(self):
        """ Disconnect FTP session """
        try:
            self.ftp.quit()
        except:
            pass

    def ftpconnect(self):
        """ Connect FTP sessin """
        try:
            if self.validate_ip(self.hostname):
                ip_addr = self.hostname
            else:
                ip_addr = self.nslookup(self.hostname)
            if is_tpnns:
                with netns.NetNS(nsname='global-vrf'):
                    ftp = ftplib.FTP(ip_addr, self.username, self.password)
            else :
                ftp = ftplib.FTP(ip_addr, self.username, self.password)
        except ftplib.all_errors as e:
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("Failed to connect to FTP server")
            logger.error("\n%s\n" % e)
            exit_with_errors()
        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("Failed while connecting to FTP server")
            logger.error("\n%s\n" % e)
            exit_with_errors()
        return ftp

    def TranslateURL(self, old_url):
        """Translates a URL possibly containing a hostname into ip based url"""
        p = list(urlparse(old_url))
        netloc = p[1]
        if self.validate_ip(netloc):
            url = old_url
        else:
            p[1] = self.nslookup(netloc)
            url = urlunparse(p)
        return url

    def filednld(self, options, packages=[], version=None):
        """ Download the package from server """

        return self.parselistdir(options, packages, packages)
 
    def httplistdir(self, options, packages=[], version=None):
        """ Download the package listfrom http server """
        files = []
        srcdir = os.path.join("/", self.srcdir)
        htmlpage = '/tmp/temphttpfilelist'
        url = self.TranslateURL(options.repository)
        cmd = netns_cmd + '/usr/bin/curl -o %s  %s/'%(htmlpage, url)
        status,output = commands.getstatusoutput(cmd)
        if status :
            logger.error("Failed to connect to HTTP(s) server :%s"%(options.repository))
            logger.error("\n%s\n" % output)
            exit_with_errors()
        parser = ListDirHTML()
        fd = open(htmlpage,'r')
        parser.feed(fd.read())
        files = parser.package_list
	if not files:
           logger.error("Unable to get listing of directory.\n"
                "It could be for one of the following reasons:-\r\n"
                "1. Path is invalid.\r\n"
                "2. Permissions not correct on given path.\r\n"
                "3. The webserver administrator has not enabled indexing.\r\n"
                "Indexing can be enabled by adding to webserver conf file:-\r\n"
                "    Options +Indexes\r\n")

        return self.parselistdir(options, packages, files)

    def tftplistdir(self, options, packages=[], version=None):
        #since we don have way to fins files/packages in tftp, assume 
        #packages are present
        return self.parselistdir(options, packages, packages)

    def ftplistdir(self, options, packages=[], version=None):
        """ Download the packages from ftp server """
        files = []
        srcdir = os.path.join("/", self.srcdir)
        # Do ftp disconnect, connect and list.
        # Ftp automatically detects a timeout and logs out the user
        # causing list to fail.
        self.quit()
        try :
            self.ftp = self.ftpconnect()
            self.ftp.cwd(srcdir)
        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("Error : Failed to read dir %s, it could be incorrect path\
                    \n\tor incorrect access rights."""%(srcdir))
            exit_with_errors()

        if is_tpnns:
            with netns.NetNS(nsname='global-vrf'):
                line = self.ftp.retrlines('LIST', files.append)
        else :
            line = self.ftp.retrlines('LIST', files.append)
	if re.search("failed to open directory", line) :
            logger.error("Warning : %s is empty, OR has insufficient access permissions."%(srcdir))
        return self.parselistdir(options, packages, files)

    def locallistdir(self, options, packages=[], version=None):
        """ List files in local disk repository """
        srcdir = os.path.join("/", self.srcdir)
        output = ''
        try:
            output = [x for x in os.listdir (srcdir) if os.path.isfile(os.path.join(srcdir, x))]
        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error(output)
            logger.error("Failed to list files in %s" % (srcdir))
            exit_with_errors()
        return self.parselistdir(options, packages, output)

    def sftplistdir(self, options, packages=[], version=None):
        """ List files in SFTP repository """
        srcdir = os.path.join("/", self.srcdir)
        pwd = self.password
        usr = self.username
        if self.validate_ip(self.hostname):
            host = self.hostname
        else:
            host = self.nslookup(self.hostname)

        cmd = netns_cmd + '/usr/bin/sshpass -p \'%s\' /usr/bin/ssh %s@%s ls  -al %s' % (pwd, usr, host, srcdir)
        output = ''
        try:
            status, output = commands.getstatusoutput(cmd)
            if status:
                logger.error(output)
                logger.error("Failed to list files in %s" % (srcdir))
                exit_with_errors()

        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error(output)
            logger.error("Failed to list files in %s" % (srcdir))
            exit_with_errors()
        return self.parselistdir(options, packages, output)

    def parselistdir(self, options, packages=[], output=''):
        """
        :param options: User options
        :param packages: Intended package list
        :param output:
        :return: List of matching package files
        Parse the files in repo and store in self.repopkgs
        """
        package_formats = ['.pkg', '.smu', '.rpm', '.iso']
        schemes_without_ls = ['tftp', 'http', 'https']
        self.messaged = False
        files = []

        logger.debug("Packages in repo :\n")
        if type(output) == type([]):
            files = output
        else:
            files = output.splitlines()
            # We are only interested in known file formats
            files = [x for x in files if any(s in x for s in package_formats)]
        if not len(files):
            logger.error("List of files in %s is empty" % ("/" + self.srcdir))
            exit_with_errors()
        if options.to_version:
            to_release = "r%s"%(options.to_version.replace('.',''))
             
        for file in files:
            fo = package.Package()
            if self.scheme not in schemes_without_ls :
                fl = file.split()
                fo.filename = fl[-1]
                fo.size = 0
            else :
                fo.filename = file.strip()
                fo.size = 0
            if '.rpm' in fo.filename:
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', fo.filename)
                if m:
                    (path, name, version, release, arch, _) = m.groups()
                    #smu has ddts id in release strip that
                    stripped_rel = self.get_release_id(release) 
                    if '.admin.' in fo.filename :
                        stripped_rel = stripped_rel.replace ('.admin', '')
                    if '.host.' in fo.filename :
                        stripped_rel = stripped_rel.replace ('.host', '')
                    if not options.to_version or to_release == stripped_rel or package.is_tp_pkg(self.pkgstate.platform, fo.filename):
                        fo.name = name
                        fo.version = version
                        fo.release = release
                        fo.arch = arch
                        fo.name_on_router = "%s-%s-%s"%(fo.name,fo.version,fo.release)
            elif '.pkg-' in fo.filename:
                '''e.g. : ncs6k-mgbl.pkg-5.2.1'''
                m = re.search(r'(.*/)*(.*)\.(pkg)-(.*)', fo.filename)
                if m :
                    (path, name, extention, version) = m.groups()
                    fo.name = name
                    fo.version = version
                    fo.arch = 'x86_64'

            elif '.iso' in fo.filename:
                '''sunstone-mini-x.iso-6.0.0'''
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)-(.*)-(.*)\.(iso)', fo.filename)
		if m:
                    (path, name, bundle_type, arch, version, label, extension) = m.groups()
                    fo.name = name + '-' + bundle_type
                    fo.version = version
                    fo.release = version
                    fo.arch = arch
                    fo.name_on_router = fo.filename.replace('.iso','')

		else:
                    m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(iso)-(.*)', fo.filename)
                    if not m :
                        m = re.search(r'(.*/)*(.*)-(.*)-(.*)-(.*)\.(iso)', fo.filename)
                    if m  and fo.filename.endswith('.iso'):
                    	(path, name, bundle_type, arch, version, extension) = m.groups()
                    elif m :
                    	(path, name, bundle_type, arch, extention, version) = m.groups()
                    	if 'golden' in fo.filename: 
                            version = '.'.join(version.split('.')[:-1])
                    if m: 
                    	fo.name = name + '-' + bundle_type
                    	fo.version = version
                    	fo.release = version
                    	fo.arch = arch
                    	fo.name_on_router = fo.filename.replace('.iso','')
                    	if 'golden' in fo.name_on_router : 
                            if golden_iso_label_name:
                               fo.name_on_router = '.'.join(fo.name_on_router.split('.')[:-1])
                               fo.name_on_router = fo.name_on_router + '-' + golden_iso_label_name
                            else:
                               fo.name_on_router = fo.name_on_router

            elif '.smu' in fo.filename:
                '''ncs6k-5.2.1.CSCur86643.smu'''
                m = re.search(r'(.*/)*(.*)-(.*)\.(.*)\.(smu)', fo.filename)
                if m :
                    (path, name, version, ddts, extention) = m.groups()
                    fo.name = name + '-' + version + '.' + ddts
                    fo.version = version
                    fo.release = version
                    #fo.arch = arch # No arch for panini SMU

            if fo.name:
                logger.debug(fo.filename)
                self.repopkgs.append(fo)
        # Along with packages, add base packages also fo TP SMU
        filelist = self.getpkgs(options, packages)
        base_packages = self.add_tpbase_smu(filelist)
        if base_packages :
            filelist.extend(self.getpkgs(options, base_packages))
        filelist = self.pick_one_version(filelist)
        return filelist

    def is_smu(self,id):
        if re.search('CSC[a-z][a-z]\d{5}',id):
            return True
        return False

    def get_release_id(self,release):
        smu = re.search('CSC[a-z][a-z]\d{5}',release)
        if smu :
            release = re.sub('.CSC[a-z][a-z]\d{5}','',release)
        return release

    def release_check_passed(self,p,package ):
        release = self.get_release_id(p)

        #SMU must have same release as that of base package 
        if self.is_smu(package.filename):
             smu_release = self.get_release_id(package.release)
             if release == smu_release:
                 return True
             else :
                 return False

        # If restrict-release options is not selected and package name is full
        # do not care about release
        if not self.options.restrict_release and p == package.filename :
            return True

        # with restricted release, package should have same release as the base
        # already installed or release of mini
        elif release == package.release : 
            return True
        elif self.options.restrict_release :
            return False

        return True

    def is_async(self,this_pkg):
        '''
            this def determines if given package is async package.
            a package is considered async if
                1. if it is an rpm
                2. it is not a yocto/thirdparty package
                3. if it is not a smu
                4. same pkg of different version/release is already installed
        '''
        platform = self.pkgstate.platform
        if not this_pkg.endswith('.rpm') or \
           package.is_tp_pkg(platform, this_pkg):
            return False
        m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', this_pkg)
        if m:
             p_name = m.groups()[1]
             p_ver  = m.groups()[2]
             p_rel  = m.groups()[3]
             active_rpms = [ x.name for x in self.pkgstate.active_package if x.name \
                 == p_name and ( not x.version == p_ver or not x.release == p_rel) \
                 and not (self.is_smu(x.release) or self.is_smu(p_rel))]
             if active_rpms :
                 logger.info("INFO: %s of different version/release is already"
                             " active"%(' '.join(set(active_rpms))))
                 return True
        return False

    def pick_one_version(self,filelist): 
        """ In case there are multiple TP SMUs pick up latest one """
        tmp_filelist = filelist[:]
        seen_pkgs = {}
        for f in tmp_filelist :
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)\.(.*)(\.rpm)',f.filename )
            if not m:
                continue
            vmtype = m.groups()[4]
            arch   = m.groups()[5]
            nh = "%s-%s-%s"%(f.name,vmtype,arch)

            if not self.is_smu(f.filename) :
                continue
            if not seen_pkgs.has_key(nh) :
                seen_pkgs[nh] = f.filename
            else :
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)\.(.*)(\.rpm)',seen_pkgs[nh])
                if not m:
                    continue
                n1 = m.groups()[1] 
                v1 = m.groups()[2]
                if not self.is_smu(f.filename) or not self.is_smu(seen_pkgs[nh]):
                    continue
                if ( self.pkgstate.platform not in f.name and 'iosxr' not in f.name ) \
                    or 'sysadmin-hostos' in f.name or 'spirit-boot' in f.name :
                    n2 = f.name 
                    v2 = f.version
                    r = rpmutils.compare_rpm_ver (v1, v2)
                    if r > 0 or r == 0 :
                        if f in filelist :
                            filelist.remove(f)
                    elif r < 0 :
                        for file in filelist :
                            if file.filename == seen_pkgs[nh] :
                                if file in filelist :
                                    filelist.remove(file)
                                break
        return filelist

    def tp_smu(self,smu_name):
        if not self.is_smu(smu_name):
            return False
        elif self.pkgstate.platform in smu_name or 'iosxr' in smu_name :
            return False
        return True

    def add_tp_base(self, to_download):
        ''' This is temp and function should be removed once install add is
        fixed to not fail if base package of SMU is in repo'''
        new_download = to_download[:]
        repo_pkgs = [ os.path.basename(p) for p in self.pkgstate.rpminrepo ]
        for package in to_download : 
            if self.tp_smu(package.filename) :
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)\.(.*)(\.rpm)',package.filename )
                if m:
                     vmtype = m.groups()[4]
                     try :
                         cmd="get_rpms.py -v %s -b %s"%(vmtype,package.filename)
                         status,base_package = commands.getstatusoutput(cmd)
                         if not base_package :
                             logger.debug("Skipping %s, it's not appilcable ??"%(file.filename))
                             continue
                         else :
                             name,arch = os.path.splitext(base_package)
                             new_name = "%s.%s%s.rpm"%(name,vmtype,arch)
                             repo_name = "%s.%s%s"%(name,vmtype,arch)
                             if repo_name in repo_pkgs :
                                 package2 = copy.deepcopy(package)
                                 package2.filename = new_name
                                 new_download.append(package2)
                                 self.redownload.append(new_name)
                     except:
                         exc_info = sys.exc_info()
    	                 TB = traceback.format_exc()
                         logger.debug(TB)
        return new_download

    def add_tpbase_smu(self, filelist):
        base_packages = []
        tmp_filelist = filelist[:]
        file_name_list = [fname.filename for fname in filelist] 
        for file in tmp_filelist: 
            if self.pkgstate.platform in file.filename and 'sysadmin-hostos' in file.filename:
                # This is host OS package needs to be handled diffrently
                # If it's admin make sure host is also there 
                if '.admin.' in file.filename :
                    filename = file.filename.replace('.admin.','.host.')
                    if filename not in file_name_list :
                        base_packages.append(filename)
                # If it's host make sure admin is also there 
                if '.host.' in file.filename :
                    filename = file.filename.replace('.host.','.admin.')
                    if filename not in file_name_list :
                        base_packages.append(filename)
                continue
            if self.pkgstate.platform in file.name :
                # There is no special handling of other Cisco packages
                continue    
            elif self.is_smu(file.filename):
                # For third party packages bases package is needed to be pulled
                # in
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)\.(.*)(\.rpm)',file.filename )
                if m:
                     vmtype = m.groups()[4]
                     try :
                         base_package = commands.getoutput("get_rpms.py -v %s -b \
                            %s"%(vmtype, file.filename)) 
                         if not base_package :
                             logger.info("Skipping %s, it's not appilcable ??"%(file.filename))
                             filelist.remove(file)
                             continue
                         else :
                             name,arch = os.path.splitext(base_package)
                             base_packages.append(name + '.'  + vmtype + arch + '.rpm')
                     except:
                         exc_info = sys.exc_info()
                         TB = traceback.format_exc()
                         logger.debug(TB)
        return base_packages

    def smu_in_input(self,this_package):
        ''' If this_package is SMU or another packages belonging to same 
            package is SMU.
        '''
        packages = self.options.userpkgs[:]
        if this_package.endswith('.rpm'):
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', this_package)
            if m:
                tp_name = m.groups()[1]
        else :
            tp_name = this_package

        if self.is_smu(this_package) :
            return True
        else :
            for package in packages : 
                if self.is_smu(package): 
                    return True
                if package.endswith('.rpm'):
                    m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', package)
                    if m:
                        package_name = m.groups()[1]
                        if tp_name == package_name and self.is_smu(package): 
                            return True
        return False

    def is_in_cli(self,package):
        packages = self.options.userpkgs[:]
        if package in packages :
            return True
        return False

    def mini_is_in_tftp(self,mini_image):
        image = "/misc/disk1/tftpboot/" + mini_image.strip()
        cmd =  "run ls " + image
        cmd = get_cal_cmd(cmd)
        try:
            status,output = commands.getstatusoutput(cmd)
            if not status:
                if 'cannot access' in output : 
                    return False
                elif image in output :
                    return True
            # If failed we assume its not there as there is no functional imact
            return False
        except :
            # If failed we assume not found as that safe, no functional impact
            return False

    def not_in_localrepo(self,pkgs):
        new_pkglist = list();
        repo_pkgs = [ os.path.basename(p) for p in self.pkgstate.rpminrepo ]
        for p in pkgs :
            ## If the pkg name contains .iso, remove it from the name.
            ## This is done because the packages on the system will not
            ## have '.iso' in them, after they are 'added'. So, the 
            ## package name comparison fails if '.iso' is not stripped
            if re.search(r'\.iso',p.filename):
                j = p.filename.replace(".iso", "");
                if j not in repo_pkgs:
                    new_pkglist.append(p);
                elif '-mini-' in j :
                    if not self.mini_is_in_tftp(j) :
                        new_pkglist.append(p);
            else:
                if os.path.splitext(p.filename)[0] not in repo_pkgs:
                    new_pkglist.append(p);
        return new_pkglist
 
    def not_on_box(self,pkgs):
        """ Package on box will either be active or inactive """
        p1 = pkgs[:]
        for p in p1 :
            for act_pkg in self.pkgstate.active_package :
                if p == act_pkg.filename :
                    logger.debug("%s is already on box as active "%(p))
                    if p in pkgs :
                        pkgs.remove(p)
        p1 = pkgs[:]
        for p in p1 :
            for inact_pkg in self.pkgstate.inactive_package :
                if p == inact_pkg.filename :
                    logger.debug("%s is already on box as inactive "%(p))
                    self.pkgstate.skipped_as_inactive.append(p)
                    if p in pkgs :
                        self.pkgstate.to_activate.append(inact_pkg)
                        pkgs.remove(p)
        return pkgs

    def filter_sps(self, options, sp_list):
        ''' In case of when there is no package specified, this function
            will filter sps wrt to v1 image in case of update and wrt v2
            in case of v2 image'''

        to_version = options.to_version
        from_version = options.from_version

        sp_names_list = [sp.filename for sp in sp_list]
        sp_names_list = list(set(sp_names_list))

        platform = self.pkgstate.platform
        new_sp_list = sp_names_list[:] 
        for sp in sp_names_list:
            m = re.search(r'(.*)-(\d+.\d+.\d+.*)\.iso', sp)
            if m:
                name = m.groups()[0]
                if platform not in name:
                    new_sp_list.remove(sp)
                    continue
                version = m.groups()[1]
                if to_version != None and version != to_version:
                    new_sp_list.remove(sp)
                    continue
                elif to_version == None and from_version != version:
                    new_sp_list.remove(sp)
                    continue

        if new_sp_list:
            sorted_sps = sorted(new_sp_list,                                                              
                               key=functools.cmp_to_key(sp_version_string_cmp))           
            latest_sp_name = sorted_sps[0]      
            latest_sp = [sp for sp in sp_list if sp.filename == latest_sp_name]

            return latest_sp
        else:
            return []

    def getpkgs(self, options, packages=[]):
        """
        :param options: from users
        :param packages: list of packages/pattern of interest
        :return:List of packages matched the options
        """
        version = options.to_version
        filelist = []
        seen_pkgs = []
        packages = list(set(packages))
        input_pkgs = packages[:]
        provider_of = self.pkgstate.hints.whatprovides
        platform =  self.pkgstate.platform
        p_name = None 
        p_version = None
        p_release = None
        # If no packages and version is specified assume intention
        # Upgrade the installed packages with newer version of SMUs
        logger.debug("RPMs in repo :\n\t %s"%("\n\t".join([x.filename for x in self.repopkgs])))
        for ip in input_pkgs:
            if ip.endswith('.tar'):
                logger.error("Error: upgrade/update of tar not supported :(%s) "%(ip))
                exit_with_errors()
        if not packages and options.update:
            # Only SMUs to update
            # Release should be same as release package
            platform =  self.pkgstate.platform
            active_rpms = [ x.name for x in self.pkgstate.active_package ]
            sp_list = []
            for p in self.pkgstate.active_package:
                if self.is_smu(p.filename) :
                    continue 
                release = p.release
                p_name = p.name
                for fo in self.repopkgs:
                    if fo.filename in seen_pkgs:
                        continue
                    if check_if_sp(fo.filename) and fo.filename not in active_rpms:
                        sp_list.append(fo)
                        seen_pkgs.append(fo.filename)
                    if not self.is_smu(fo.filename):
                        continue
                    if not self.messaged:
                        logger.info("SMUs for all installed packages")
                        self.messaged = True
    
                    smu_release = self.get_release_id(fo.release)
                    if 'sysadmin-hostos' in p_name or (platform not in p_name \
                    and 'iosxr' not in p_name):
                        if p_name == fo.name and 'sysadmin-hostos' not in p_name :
                            if release+'.host' == smu_release or release+'.admin'\
                            == smu_release or release+'.xr' == smu_release or \
                            release == smu_release :
                                filelist.append(fo)
                                seen_pkgs.append(fo.filename)

                        elif p_name == fo.name and 'sysadmin-hostos' in p_name :
                            if release == smu_release :
                                filelist.append(fo)
                                seen_pkgs.append(fo.filename)

                    elif p_name == fo.name and release == smu_release :
                        # SMu of mandatory package
                        filelist.append(fo)
                        seen_pkgs.append(fo.filename)
                    elif p_name == provider_of(fo.name,platform) and release == smu_release:
                        filelist.append(fo)
                        seen_pkgs.append(fo.filename)
            filelist += self.filter_sps(options, sp_list)

        # If package is specified without version, assumed intention
        # is upde the smus of that package or package itself if not already
        # installed or it could be SMU id
        elif packages and options.update:
            active_rpms = [ x.name for x in self.pkgstate.active_package ]
            active_rpms_name = [x.filename for x in self.pkgstate.active_package]
            release = 'r' + self.v1.replace('.','')
            for ip in input_pkgs:
                if platform+"-mini" in ip or platform+"-full" in ip :
                    logger.error("Error:install update should be used for\n"
                                 "smu's and optional packages. Please try \n"
                                 "install ugrade if you intend to upgrade (%s) "%(ip))
                    exit_with_errors()
            for p in packages:
                p_name = p
                ''' process the service pack'''
                if check_if_sp(p) == True and p not in seen_pkgs:
                    for fo in self.repopkgs:
                        if fo.filename == p:
                            if fo.name in active_rpms:
                                self.pkgstate.skipped_as_active.append(p)
                                seen_pkgs.append(fo.filename)                   
                                input_pkgs.remove(p) 
                                continue
                            filelist.append(fo)
                            if p in input_pkgs :
                                seen_pkgs.append(fo.filename)
                                input_pkgs.remove(p)
                            
                elif p_name.endswith('.rpm'):
                    m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', p)
                    if m:
                        p_name = m.groups()[1]
                        p_version = m.groups()[2]
                        p_release = m.groups()[3]
                        release = m.groups()[3]
                if p_name in active_rpms :
                    for arpm in self.pkgstate.active_package :
                        if arpm.name == p_name :
                            release = self.get_release_id(arpm.release)
                            break
                for fo in self.repopkgs:
                    smu_release = self.get_release_id(fo.release) 
                    provider = provider_of(fo.name,platform) 
                    # If package name , or full package name matches package in repo
                    # and package name or provider of package name is active 
                    if (p == fo.filename or p_name == provider or p == fo.name \
                        or p_name == fo.name ) \
                        and (p_name in active_rpms or provider in active_rpms):
                        if not self.is_async(p):
                            if self.smu_in_input(p) and self.is_smu(fo.filename):
                                # If SMU is specified in input list, only given SMU
                                # should be used , as it's user is selecting SMU
                                # from any available SMUs
                                # IMP we match only name-version-release to pick
                                #     up different arch SMUs also
                                if p in active_rpms_name:
                                    if p in input_pkgs :
                                        input_pkgs.remove(p)
                                    self.pkgstate.skipped_as_active.append(p)
                                    continue
                                p_nvr = "%s-%s-%s"%(p_name,p_version,p_release)
                                f_nvr = "%s-%s-%s"%(fo.name,fo.version,fo.release)
                                if p_nvr == f_nvr and fo.filename not in seen_pkgs: 
                                    filelist.append(fo)
                                    if p in input_pkgs :
                                        seen_pkgs.append(fo.filename)
                                        input_pkgs.remove(p)
                                    if fo.filename in input_pkgs :
                                        input_pkgs.remove(fo.filename)
                                if fo.name == p_name and \
                                   package.is_tp_pkg(self.pkgstate.platform, fo.filename):
                                    if smu_release == release+'.host' or \
                                    smu_release == release+'.admin' or \
                                    smu_release == release+'.xr' or smu_release\
                                    == release: 
                                        filelist.append(fo)
                                        seen_pkgs.append(fo.filename)
                                        if fo.filename in input_pkgs :
                                            input_pkgs.remove(fo.filename)

                            # Same release package is already installed so it
                            # should be SMU 
                            elif self.is_smu(fo.filename) and release == self.get_release_id(fo.release):
                                # If it's SMU , it's not enough to match name and
                                # release ID , version also should match , do
                                # not compare full file name as same SMU from
                                # different arch are to be picked up
                                if self.is_smu(p) and not p_version == fo.version :
                                    continue

                                if not fo.filename in seen_pkgs: 
                                    filelist.append(fo)
                                    if p in input_pkgs :
                                        input_pkgs.remove(p)
                                        seen_pkgs.append(fo.filename)
                            elif 'sysadmin-hostos' in p_name or (platform not \
                            in p_name and 'iosxr' not in p_name):
                                #If hostos package, release is release of active rpm
                                if p_name == fo.name and 'sysadmin-hostos' in p_name :
                                    # hostos package will always have .host or .admin
                                    if smu_release == release:
                                        filelist.append(fo) 
                                        seen_pkgs.append(fo.filename) 
                                        if p in input_pkgs : 
                                            input_pkgs.remove(p) 
                                if p_name == fo.name and 'sysadmin-hostos' not in p_name :
                                    # This is TP package/SMU base package rel 
                                    # does not contain VM
                                    if smu_release == release+'.host' or \
                                    smu_release == release+'.admin' or \
                                    smu_release == release+'.xr' or smu_release\
                                    == release: 
                                        filelist.append(fo) 
                                        seen_pkgs.append(fo.filename) 
                                        if p in input_pkgs and p == fo.filename: 
                                            input_pkgs.remove(p)
                                if p == fo.filename :
                                    #If explicit full name of TP/hostos is given
                                    filelist.append(fo) 
                                    seen_pkgs.append(fo.filename) 
                                    if p in input_pkgs : 
                                        input_pkgs.remove(p) 
                            else :
                                #Same release can not have two same packages
                                # Since already active don't care
                                if p_version and ( p_version == fo.version and \
                                    p_release == fo.release ) :
                                    if p in input_pkgs :
                                        input_pkgs.remove(p)
                                        logger.info(""" 
                                        %s is ignored as there is active package from same release."""%(p))
                                        self.pkgstate.skipped_as_active.append(p)
                                elif not p.endswith('.rpm'):
                                    # Only package name is given and that's
                                    # active so skip it
                                    if p in input_pkgs :
                                        input_pkgs.remove(p)
                                        logger.info("%s is active, so skipping it"%(p))
                                        self.pkgstate.skipped_as_active.append(p)
                                continue
                        else :
                            #It's different release than active package, it's
                            #async package upgrade
                            logger.error("There can be only one instance of XR"
                                         " package installed and all the XR"
                                         " packages must be from same release."
                                         " Please remove %s from input list"%(p))
                            exit_with_errors()
                            '''
                            commenting this code as if in future async upgrade
                            becomes a reality, you don't need to reinvent the 
                            wheel and can just uncomment below lines
                            if p in input_pkgs:
                                input_pkgs.remove(p)
                            if not fo.filename in seen_pkgs:
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                                input_pkgs.remove(fo.filename)
                            '''    
                        
                    elif p_name not in active_rpms and (p_name == provider or p == fo.name or p == fo.filename) :
                        # Install the optional package and it's SMUs
                        # It is first time optional package installation or asyn
                        # package installation
                        if self.smu_in_input(p) and self.is_smu(fo.filename):
                            # If SMU is specified in input list, only given SMU
                            # should be used , as it's user is selecting SMU
                            # from any available SMUs
                            # IMP we match only name-version-release to pick
                            #     up different arch SMUs also
                            p_nvr = "%s-%s-%s"%(p_name,p_version,p_release)
                            f_nvr = "%s-%s-%s"%(fo.name,fo.version,fo.release)
                            if p_nvr == f_nvr :
                                if fo.filename not in seen_pkgs: 
                                    seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                                if p in input_pkgs :
                                    input_pkgs.remove(p)
                            else :
                                continue

                        logger.debug("Optional package %s and it SMUs to be installed"%(p))
                        if self.get_release_id(release) == self.get_release_id(fo.release):
                            # if only package name is speified , release will be
                            # as base package
                            if p in input_pkgs:
                                input_pkgs.remove(p)
                            if not fo.filename in seen_pkgs:
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                        else :
                            logger.debug("Optional package %s did not match release"%(p))
                    elif p_name not in active_rpms and (p_name == provider or p_name == fo.name) :
                        #Automatic pull SMUs for packages when full name specified  
                        # Do not pull SMUs of package if the package was pulled                         # in due to dependency
                        if not self.is_in_cli(p):
                            continue
                        if not self.is_smu(fo.filename) :
                            continue
                        elif self.smu_in_input(p):
                            continue
                        elif p_release == self.get_release_id(fo.release) :
                            if p in input_pkgs:
                                input_pkgs.remove(p)
                            if not fo.filename in seen_pkgs:
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)

            if not self.messaged:
                logger.info("\nUpdate packages :\n\t%s" % ('\n\t'.join(packages)))
                self.messaged = True

        # If only version is specified with upgrade operation
        # it's assumed that package list is same as existing
        elif not options.userpkgs and options.upgrade:
            active_rpms = [ x.name for x in self.pkgstate.active_package ]
            sp_list  = []
            for p in packages :
                release = "r%s"%(version.replace('.',''))
                p_name = p
                if p_name.endswith('.rpm'):
                    m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', p)
                    if m:
                        p_name = m.groups()[1]
                for fo in self.repopkgs :
                    if fo.filename not in seen_pkgs:
                        continue
                    if check_if_sp(fo.filename):
                        sp_list.append(fo)
                        seen_pkgs.append(fo.filename)
                    if self.is_smu(fo.filename):
                        # IN upgrade case there can't be SMU without a pckage in
                        # input list, so not removing in case of SMU. It gets
                        # removed in package below
                        smu_release = self.get_release_id(fo.release)
                        if 'sysadmin-hostos' in p_name and p_name == fo.name :
                            # We don's support TP SMU during upgrade if not
                            # specified in CLI
                            if release == smu_release :
                                filelist.append(fo)
                                seen_pkgs.append(fo.filename)
                                if p in input_pkgs :
                                    input_pkgs.remove(p)
                        elif fo.name in active_rpms and release == smu_release :
                            # SMu of mandatory package
                            filelist.append(fo)
                            seen_pkgs.append(fo.filename)
                            if p in input_pkgs and self.is_smu(p):
                                input_pkgs.remove(p)
                        elif p_name == provider_of(fo.name,platform) and release == smu_release:
                            filelist.append(fo)
                            seen_pkgs.append(fo.filename)
                            if p in input_pkgs and self.is_smu(p):
                                input_pkgs.remove(p)
                    else :
                        if p == fo.name or p == fo.filename:
                            pkg_release = self.get_release_id(fo.release)
                            # Optional packages or SMUs
                            if release == pkg_release and fo.filename not in seen_pkgs:
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                                if p in input_pkgs :
                                    input_pkgs.remove(p)
                            # Mini ISO 
                            elif '-mini' in p :
                                if version == fo.version and fo.filename not in seen_pkgs:
                                    filelist.append(fo)
                                    if p in input_pkgs :
                                        seen_pkgs.append(fo.filename)
                                        input_pkgs.remove(p)
                filelist += self.filter_sps(options, sp_list)
        # If version and package list is specified with upgrade
        # operation CLI should contain all packages including mini,
        # no assumptions are made for package list. Though latest
        # SMUs of packages being installed will be picked up.
        elif packages and options.upgrade:
            active_rpms = [ x.name for x in self.pkgstate.active_package ]
            sp_in_input = any([pkg for pkg in packages if check_if_sp(pkg)]) 
            sp_list = []
            provider_of = self.pkgstate.hints.whatprovides
            platform =  self.pkgstate.platform
            for p in packages :
                release = "r%s"%(version.replace('.',''))
                logger.debug("Input package : %s"%(p))
                p_name = p
                ''' process the service pack'''
                if check_if_sp(p) == True and p not in seen_pkgs:
                    for fo in self.repopkgs:
                        if fo.filename == p:
                            filelist.append(fo)
                            if p in input_pkgs :
                                seen_pkgs.append(fo.filename)
                                input_pkgs.remove(p)
                if p_name.endswith('.rpm'):
                    m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', p)
                    if m:
                        p_name = m.groups()[1]
                        p_version = m.groups()[2]
                        p_release = m.groups()[3]
                        release = m.groups()[3]
                for fo in self.repopkgs :
                    '''process the service pack'''
                    if sp_in_input == False and check_if_sp(fo.filename)\
                                    and fo.filename not in seen_pkgs:
                        sp_list.append(fo)
                        seen_pkgs.append(fo.filename)
                        if p in input_pkgs :
                            input_pkgs.remove(p)

                    renamed_mini_file = ''
                    if "-mini" in fo.name or '-golden' in fo.name :
                        renamed_mini_file = fo.filename.replace('.iso','')
                        mini_ver = renamed_mini_file.split('-')[-1]
                        mini_name = renamed_mini_file.split('-')[:-1]
                        mini_name = '-'.join(mini_name)
                        if fo.filename.endswith('.iso') :
                            renamed_mini_file = "%s.iso-%s"%(mini_name,mini_ver)
                        elif '.iso' in fo.filename :
                            renamed_mini_file = "%s-%s.iso"%(mini_name,mini_ver)

                    if self.is_smu(fo.filename):
                        smu_release = self.get_release_id(fo.release)
                        if p == fo.filename and ( self.pkgstate.update_giso or \
                            self.pkgstate.upgrade_giso ):
                            # Giso and CLI contails package we need to honour it
                            filelist.append(fo)
                            if fo.filename not in seen_pkgs :
                                seen_pkgs.append(fo.filename)
                            if p in input_pkgs :
                                input_pkgs.remove(p)

                        elif fo.name in active_rpms and (release == smu_release\
                            or release == fo.release ) and not \
                            ( self.pkgstate.update_giso or self.pkgstate.upgrade_giso ):
                            # SMu of mandatory package
                            filelist.append(fo)
                            if p in input_pkgs and self.is_smu(p):
                                input_pkgs.remove(p)
                        elif fo.name in active_rpms and not \
                            ( self.pkgstate.update_giso or self.pkgstate.upgrade_giso ):
                            if 'sysadmin-hostos' in fo.name or ( platform not \
                            in fo.name and 'iosxr' not in fo.name):
                                # v2_release is release to what is being upgraded
                                # or specified TP/hostos package and smu_release 
                                # is release ID of file
                                v2_release = self.get_release_id(release)
                                if v2_release + '.host'  == smu_release  or \
                                    v2_release + '.admin'  == smu_release or \
                                    v2_release + '.xr'  == smu_release  or \
                                    v2_release == smu_release :
                                    filelist.append(fo)
                                    if fo.filename not in seen_pkgs :
                                        seen_pkgs.append(fo.filename)
                                    if p in input_pkgs :
                                        input_pkgs.remove(p)
                                elif p == fo.filename :
                                   # If Hostos/TP SMU is specified in CLI and present in
                                   # repo , get that                   
                                   filelist.append(fo)
                                   if fo.filename not in seen_pkgs :
                                       seen_pkgs.append(fo.filename)
                                   if p in input_pkgs :
                                       input_pkgs.remove(p)
                                   
                        elif p_name == provider_of(fo.name,platform) and \
                            release == smu_release and not \
                            ( self.pkgstate.update_giso or self.pkgstate.upgrade_giso ):
                            # SMU of optional packages
                            filelist.append(fo)
                            if p in input_pkgs and self.is_smu(p):
                                input_pkgs.remove(p)
                    elif p == fo.name or p == fo.filename or p_name == provider_of(fo.name,platform) or p == renamed_mini_file:
                        pkg_release = self.get_release_id(fo.release)
                        logger.debug("  Name and released matched %s with %s"%(p,fo.filename))
                           
                        if '-mini' in p :
                            if fo.version and version == fo.version and fo.filename not in seen_pkgs:
                                logger.debug("    Version matched with %s"%(fo.filename))
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                                if p in input_pkgs:
                                    input_pkgs.remove(p)
                        elif '-golden' in p or '-goldenk9' in p :
			    g_version = fo.version;
                            m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\d+\w*)-(\w*)\.iso', fo.filename)
                            if not m:
                                # Golden ISO CCO image version-label.iso format
                                m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+\w*)-(\w*)\.iso', fo.filename)
                            if not m:
                                m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+\w*).(\w*)', fo.filename)
                            if not m:
                                m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+.\d+\w*).(\w*)', fo.filename)
                            if not m:
                                m = re.search (r'(.*/)*(.*)-(.*).iso$', fo.filename)
                            if not m:
                                m = re.search (r'(.*/)*(.*)\.iso-(.*)', fo.filename)
			    if not m:
                            	g_version = '.'.join(fo.version.split('.')[:-1])
                            if g_version and version == g_version and fo.filename not in seen_pkgs:
                                logger.debug("    Version matched with %s"%(fo.filename))
                                seen_pkgs.append(fo.filename)
                                filelist.append(fo)
                                if p in input_pkgs :
                                    input_pkgs.remove(p)
                        elif fo.version and release == pkg_release and fo.filename not in seen_pkgs:
                            logger.debug("    Version matched with %s"%(fo.filename))
                            seen_pkgs.append(fo.filename)
                            filelist.append(fo)
                            if p in input_pkgs :
                                logger.debug("    Got %s "%(fo.filename))
                                input_pkgs.remove(p)
                                #If name and full name both are given handlei it
                                if not self.is_smu(p) and p_name and p_name in input_pkgs :
                                    input_pkgs.remove(p_name)
                                elif fo.filename in input_pkgs :
                                    # If package is pulled in due to just name
                                    # remove from input oist
                                    input_pkgs.remove(fo.filename)
                if sp_in_input == False:
                    filelist += self.filter_sps(options, sp_list)

        if input_pkgs:
            # If these are not in inactive or ative state 
            input_pkgs = self.not_on_box(input_pkgs)
            if input_pkgs:
                if options.to_version:
                    logger.info ("Following packages not found in repository")
                    logger.info (",".join(set(input_pkgs)))
                    logger.info ("Being upgrade, will check %s for availability of packages."
                                %(options.to_version))
                    self.pkgstate.unresolvedpkgs = input_pkgs[:]
                else:
                    logger.error("Error: Following packages not found in repository")
                    logger.error(",".join(set(input_pkgs)))
                    exit_with_errors()
        logger.debug( "Packages identified to add pre-arch %s "%(" ".join([f.filename for f in filelist])))
        filelist = self.multiarch(filelist)
        logger.debug( "Packages identified to add post-arch %s "%(" ".join([f.filename for f in filelist])))
        return filelist

    def multiarch(self,filelist_in):
        SYSADMIN = 'sysadmin'
        CAL = 'CAL'
        XR  = 'XR'
        platf = self.pkgstate.platform
        filelist = filelist_in[:]

        for p in filelist: 
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', p.filename)
            if m:
                (path, name, version, release, arch, _) = m.groups()
                if (platf in name and SYSADMIN in name) or \
                    ('admin' in release) or 'host' in release :
                    # It's Cisco Cal or host SMU, if platform supports multi 
                    # fetch other supported arch rpm
                    if arch not in self.pkgstate.supported_archs[CAL]:
                        filelist_in.remove(p)
                        logger.debug("Excluding %s as  it's unsupported arch."%(p.filename))
                elif (platf in name and SYSADMIN not in name) or ('.xr' in release):
                    #It's XR package 
                    if arch not in self.pkgstate.supported_archs[XR]:
                        filelist_in.remove(p)
                        logger.debug("Excluding %s as  it's unsupported arch."%(p.filename))
            else :
                continue
        return filelist_in
      
    def sftpget(self, pkgs):
        """ Download package through sftp """
        srcdir = os.path.join("/", self.srcdir)
        pwd = self.password
        usr = self.username
        if self.validate_ip(self.hostname):
            host = self.hostname
        else:
            host = self.nslookup(self.hostname)
        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        for pkg in pkgs:
            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)
            src = os.path.join("/", self.srcdir, pkg.filename)
            dest = os.path.join(self.dest, pkg.filename)

            cmd = netns_cmd + "/usr/bin/sshpass -p \'%s\' /usr/bin/sftp %s@%s:/%s %s" % (pwd, usr, host, src, dest)
            download_pkgs(cmd, src, dest, pkg.filename, self.redownload, self.hostname) 

        return 0

    def localget(self, pkgs):
        """Download packages from local directory"""
        srcdir = os.path.join("/", self.srcdir)
        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        for pkg in pkgs:
            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)
            src = os.path.join("/", self.srcdir, pkg.filename)
            dest = os.path.join(self.dest, pkg.filename)

            cmd =  "cp %s %s" % (src, dest)
            download_pkgs(cmd, src, dest, pkg.filename, self.redownload, self.hostname)
 
        return 0

    def scpget(self, pkgs):
        """ Download packages from SCP server """
        srcdir = os.path.join("/", self.srcdir)
        pwd = self.password
        usr = self.username
        if self.validate_ip(self.hostname):
            host = self.hostname
        else:
            host = self.nslookup(self.hostname)
        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        for pkg in pkgs:
            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)
            src = os.path.join("/", self.srcdir, pkg.filename)
            dest = os.path.join(self.dest, pkg.filename)
            
            cmd = netns_cmd + "/usr/bin/sshpass -p \'%s\' /usr/bin/scp %s@%s:/%s %s" % (pwd, usr, host, src, dest)
            download_pkgs(cmd, src, dest, pkg.filename, self.redownload, self.hostname)
 
        return 0

    def httpget(self, pkgs):
        if not os.path.exists(self.dest):
            try :
                os.makedirs(self.dest)
            except OSError, e :
                logger.error("Failed to create temporary staging directory on XR\n\t\t"
                             "ERROR: mkdir failed (%s : %s)\n\t\t"
                             "Please remove unwanted files on harddisk: and retry\n" % (self.dest, os.strerror(e.errno)))
                exit_with_errors()

        for pkg in pkgs:
            if pkg.filename in self.downloaded_pkgs :
                # Already downloaded
                continue
            self.downloaded_pkgs.append(pkg.filename)
            src = os.path.join(self.repo, pkg.filename)
            dest = os.path.join(self.dest, pkg.filename)

            url = self.TranslateURL(src)
            cmd = netns_cmd + "/usr/bin/curl -f -o %s %s" % (dest, url)
            download_pkgs(cmd, src, dest, pkg.filename, self.redownload, self.hostname)

        return 0

def download_pkgs(cmd, src, dest, filename, redownload, hostname):
    MAX_RETRY_COUNT = 2
    retry_count = 0

    while retry_count < MAX_RETRY_COUNT:
        try:
            retry_count = retry_count + 1
            logger.debug("retry_count: %d" %retry_count)
            if filename not in redownload and retry_count == 1:
                # dont delete below logging. Used for yang log parsing
                logger.info("Fetching .... %s" % (filename))
            if retry_count > 1:
                logger.info("Retrying .... %s" %filename)
            status, output = commands.getstatusoutput(cmd)
            if status:
                logger.debug("cmd execution failed, status is: %d" %status)
                if no_space_left.search(output) or http_nospace_left in output:
                    logger.debug("No space left on device, so no need to retry fetching") 
                    retry_count = MAX_RETRY_COUNT 
                if retry_count < MAX_RETRY_COUNT:
                    os.remove(dest)
                    logger.info("Fetching of %s failed" %(filename))
                    continue
                curl_error=output.split("curl")
                if len(curl_error) > 1:
                    logger.error("Failed to download %s\ncurl%s" % (src, curl_error[1]))
                else:
                    logger.error("Failed to download %s\n%s" % (src, output))
                dir_path = os.path.dirname(os.path.realpath(dest)) 
                shutil.rmtree(dir_path)   
                exit_with_errors()
            else:
                valid_md5sum = validate_downloaded_package_md5sum(dest)
                logger.debug("md5sum of %s is %d" %(filename, valid_md5sum))
                if not valid_md5sum:
                     logger.info("md5sum of %s not matched" %filename)
                     if retry_count < MAX_RETRY_COUNT:
                         os.remove(dest)
                         continue

                     logger.error("Failed to download %s\n%s" % (src, output))
                     dir_path = os.path.dirname(os.path.realpath(dest)) 
                     shutil.rmtree(dir_path)   
                     exit_with_errors()
                else:
                    break
        except Exception as inst:
            logger.error("""
                          Either host %s is not reachable or file %s does not exists
                          %s """ % (hostname, filename, inst.__doc__))
            exit_with_errors()
        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("Failed to download %s" % (src))
            exit_with_errors()

    #Perform disk space metric checks if applicable
    try:
        if options.replace and options.commit and not options.force \
               and is_metric_enabled_in_install_profile("disk-space"):
           do_disk_space_prechecks(dest)
    except :
        logger.debug("Failed to perform disk space checks. Continuing")
        pass

def do_disk_space_prechecks(dest):
    logger.debug("Running disk space prechecks")
    disk_space = []
    disk_space = precheck_dict['disk-space']
    if os.path.exists(dest):
       iso_size = os.path.getsize(dest) 
    else:
       #ISO has already been added to the repo, set the size to 0 
       iso_size = 0
       dest = os.path.splitext(dest)[0]
    cmd = "/pkg/bin/install_exec_sysadmin " \
    "\"/opt/cisco/calvados/bin/install-functions.py run_disk_space_checks " \
    "%s %s\"" %(iso_size, dest) 
    output = run_cmd(cmd)
    if not "Passed" in output:
       if disk_space[METRIC_ACTION] != WARN_ON_FAILURE:
          logger.error("ERROR: Disk space check failed! Install profile is configured to terminate the System Upgrade on failure.\nERROR:\n%s" %(output)) 
          exit_with_errors()
       else:
          logger.info("WARNING: Disk space check failed. Install profile is configured to warn and continue.") 
    return

def get_rp_xr_output(vm_ip):
    """This function will be used to get the output on a RP XR vm. """
    cmd = ("%s ssh -T -q %s" % (platform_prefix, vm_ip))
    sshProcess = subprocess.Popen([cmd],
                                  stdin=subprocess.PIPE,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE,
                                  shell=True)
    rm_cmd = ("rm -f %s" % (INSTALL_MARKER_FILE))
    sshProcess.stdin.write("%s\n" % (rm_cmd))
    sshProcess.stdin.close()

def run_on_all_rps_xr():
    xr_vm_info = collect_vms_info()
    lead_rp_vm = xr_vm_info.lead_xr_vm
    rps_vm = xr_vm_info.xr_vm_ip
    threads = [None] * len(rps_vm)
    i = 0
    for vm_ip in rps_vm:
        threads[i] = Thread(
                target=get_rp_xr_output, args=(vm_ip))
        threads[i].start()
        i = i + 1
    for t in threads:
        t.join()

def set_op_lock ():
    try:
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(4)
        logger.debug(cmd)
        status,output = commands.getstatusoutput(cmd)
        if not status:
            if oper_none_re.search(output):
                logger.debug("No install operation in progress on Sysadmin, proceeding..")
            elif oper_oip_re.search(output):
                print(output)
                exit_with_errors() 
            elif oper_nodestatus_re.search(output):
                print(output)
                exit_with_errors()
            else:
                #This is either internal error or WTBA found case, print the message as is
                print(output)
                unset_op_lock()
                exit_with_errors()
    except:
        logger.info ("An internal error occured while trying to acquire operation lock")
        unset_op_lock()
        exit_with_errors() 

def unset_op_lock(upd_status_del = True):
    # Unset the op_in_progress globals on Sysadmin
    # Remove any status file (at unlock).
    if upd_status_del:
        update_status_optim_prep (init = True)
    try: 
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(5)
        logger.debug(cmd)
        run_cmd(cmd)
        logger.debug ("Successfully unset op_in_progress globals")
    except:
        logger.debug ("Error encountered while trying to unset op_in_progress globals, retrying..")
        run_cmd(cmd)
    if os.path.isfile(INSTALL_MARKER_FILE):
        os.remove(INSTALL_MARKER_FILE)

def validate_iso_prepare(iso_name):
    '''
        Get version of iso.
    '''
    global golden_iso_label_name 
    m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\d+\w*)-(\w*)', iso_name)
    if not m:
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+\w*)-(\w*)', iso_name)
    if not m:
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\w*)', iso_name)
    if not m:
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+)', iso_name)
    if m:
        logger.debug("INFO: version = %s" %(m.groups()[2]))
        if len (m.groups()) > 3:
            golden_iso_label_name = m.groups()[3]
            logger.debug("INFO: Golden iso label name = %s" %(m.groups()[3]))
        return m.groups()[2]
    return None

def validate_iso(iso_name):
    '''Validate input iso with valid iso types understood by this application'''
    version = ''
    global golden_iso_label_name 
    global is_new_label_format
    iso_plat = iso_name.split('-')[0]
    valid_iso_list = [iso_plat + x for x in ISO_NAME_TYPES]
    
    # Golden ISO weekly build with version-label.iso format
    m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\d+\w*)-(\w*)\.iso', iso_name)
    if m:
        golden_iso_label_name = m.groups()[3]
        logger.info("Label = %s" %(golden_iso_label_name))
        is_new_label_format = True
    if not m and "-golden" in iso_name:
        # Golden ISO CCO image with version-label.iso format
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+\w*)-(\w*)\.iso', iso_name)
        if m:
            golden_iso_label_name = m.groups()[3]
            logger.info("Label = %s" %(golden_iso_label_name))
            is_new_label_format = True
    if not m and "-golden" in iso_name:
        # Golden ISO weekly build with .iso-version.label format
        m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+.\d+\w*)\.(.+)', iso_name)
        if m:
            golden_iso_label_name = m.groups()[3]
            if golden_iso_label_name.endswith ('.iso'):
                logger.debug ('Issue with label name %s'%(golden_iso_label_name))
                m = None
            else:
                logger.info("Label = %s" %(golden_iso_label_name))
    if not m and "-golden" in iso_name:
        m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+\w*)\.(.+)', iso_name)
        if m:
            golden_iso_label_name = m.groups()[3]
            if golden_iso_label_name.endswith ('.iso'):
                logger.debug ('Issue with label name %s'%(golden_iso_label_name))
                m = None
            else:
                logger.info("Label = %s" %(golden_iso_label_name))
    if "-golden" not in iso_name:
        if not m:
            m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+.\d+\w*)', iso_name)
        if not m:
            m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+\w*)', iso_name)
    # New format GISO do support blank label, so keeping regexes below without
    # check for golden.
    if not m:
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+)\.iso', iso_name)
    if not m:
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\w*)\.iso', iso_name)
        if m and "-golden" in iso_name:
            versionchk = m.groups()[2]
            logger.debug ("Check if we have a valid version string for %s"%(versionchk))
            buildnum = re.search (r'(.*)\.(.*)\.(.*)\.(.*)', versionchk).groups()[3]
            if buildnum and not buildnum[0].isdigit():
                # Not a valid build number in version.
                logger.debug ("Build number %s is invalid."%(buildnum))
                m = None
    if m:
        (path, name, version) = (m.groups()[0],m.groups()[1],m.groups()[2])
        if name not in valid_iso_list:
            logger.error("ISO name not in allowed format. Allowable formats are: "
                        "\n %s"%('\n '.join(valid_iso_list)))
            exit_with_errors()
        logger.debug("INFO: path = %s" %(path))
        logger.debug("INFO: name = %s" %(name))
        logger.debug("INFO: version = %s" %(version))
        return version
    return None

def issue_cmd_get_list(cmd):
    pkgs = []
    logger.debug("Cmd = %s" %(cmd))
    status,output = commands.getstatusoutput(cmd)
    if output:
        lines = output.split("\n\t\n")[0].splitlines()
        for line in lines:
            line = line.strip()
            if "Node " in line:
                continue;
            if "Packages: " in line:
                continue;
            if "Boot " in line:
                continue;
            if "Superseded Packages:" in line:
                continue;
            if "No fully superseded" in line:
                continue;
            if "superseded by" in line:
                line = line.split()[0]
            if line not in pkgs and len(line) > 0:
                pkgs.append(line)
    for p in pkgs:
        logger.debug("Pkg: %s" %(p))

    return pkgs

def check_if_sp(file_name):
    """This function returns True if the file_name is service pack else False"""
    m = re.search(r'(.*-sp\d+-.*)-(\d+.\d+.\d+.*)\.iso', file_name)
    if m: 
        return(True)
    else:
        m = re.search(r'(.*-sp\d+-.*)-(\d+.\d+.\d+.*)', file_name)
        if m: 
            return(True)
    return(False)

def get_sp_rpms(file_name):
    """This function returns the list of rpms in a sp"""
    """ using the cmd show install package """
    sp_name = os.path.splitext(file_name)[0]
    cmd = "sdr_instcmd show install package %s none" %(sp_name)                  
    output = commands.getoutput(cmd)            
    y = output.splitlines()
    rpm_list = []
    for line in y:
        m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', line.strip())
        if m:
            rpm_list.append(line.strip())
    
    return rpm_list

def get_platform():                                                         
    cmd  = "sdr_instcmd show install active | grep 'Boot image' |sort | uniq | cut -f1 -d'-'"
    try :                                                                       
        platform = commands.getoutput(cmd)                                      
        return platform.strip()                                                 
    except:                                                                     
        return None    

def sp_version_string_cmp(sp1, sp2):                                            
    if (not sp1) and (not sp2):                                                 
        return 0                                                                
    list1 = os.path.basename(sp1).split('-')                                    
    list2 = os.path.basename(sp2).split('-')                                    
                                                                                
    spv1 = int(re.findall(r'\d+', list1[1])[0])                                 
    spv2 = int(re.findall(r'\d+', list2[1])[0])                                 
                                                                                
    if spv1 > spv2:                                                             
        return -1                                                               
    elif spv1 < spv2:                                                           
        return 1                                                                
    else:                                                                       
        return 0    

def get_active_xr_pkgs ():
    xr_active = []
    xr_active_pkgnames = []
    platform = get_platform()
    if platform == None:
        logger.error("Unable to get platform. Exiting!")
        exit_with_errors()
    xra_cmd = "sdr_instcmd show install active"
    xr_active = issue_cmd_get_list(xra_cmd); 
    for pkg in xr_active:
        m = re.search(r'(.*/)*(.*)-(.*)-(.*)', pkg)
        if m:
            (path, name, version, release) = m.groups()
            if platform in name and not re.search(r'.CSC',pkg):
                # We are only looking for XR optional packages.
                if not "spirit-boot" in name: 
                    xr_active_pkgnames.append (name)
    return set(xr_active_pkgnames)

def get_active_sp():
    """ this will get the Service Pack which is active"""
    xra_cmd = "sdr_instcmd show install active"
    ada_cmd = "/pkg/bin/sdr_actioncmd active"

    xr_active = issue_cmd_get_list(xra_cmd);
    sp = list(filter(check_if_sp, [x.strip() + ".iso" for x in xr_active]))
    if not sp:
        admin_active = issue_cmd_get_list(ada_cmd);
        sp = list(filter(check_if_sp, [x.strip() + ".iso" for x in admin_active]))

    return sp[0] if sp else ""

def process_sp_list(sp_list,pkglist,from_version, to_version):
    """this will just choose the latest sp and skip all others in input list"""
    platform = get_platform()
    if platform == None:
        logger.error("Unable to get platform. Exiting!")
        exit_with_errors()
    for sp in sp_list:
        m = re.search(r'(.*)-(\d+.\d+.\d+.*)\.iso', sp)
        if m:
            name = m.groups()[0]
            if platform not in name:
                logger.error("Service pack : %s can not be used with platform %s " 
                                %(sp,platform))
                exit_with_errors()

            version = m.groups()[1]
            if to_version != None and version != to_version:
                logger.error("Service Pack compatibility failed for: %s " %(sp))
                logger.error("Service Pack with version %s can not be used " 
                             "with to be Upgrade version %s " % (version, to_version))
                exit_with_errors()
            elif to_version == None and from_version != version:
                logger.error("Service Pack compatibility failed for: %s " %(sp))
                logger.error("Service Pack with version %s can not be used " 
                             "with running s/w version %s " % (version,from_version))
                exit_with_errors()
    sorted_sps = sorted(sp_list,                                                              
                       key=functools.cmp_to_key(sp_version_string_cmp))           
    latest_sp_name = sorted_sps[0]      
    if not to_version and active_sp and sp_version_string_cmp(active_sp, latest_sp_name) == -1:
        logger.error("Lower version SP (%s) cannot be installed if a higher version"
                     " SP (%s) is active" % (latest_sp_name, \
                                               os.path.splitext(active_sp)[0]))
        exit_with_errors()
    for sp in sorted_sps[1:]:
        logger.error("Skipping the Service Pack: %s as Higher Numbered Service"
                     " Pack is available in input Service Packs" %(sp))   
    pkg_list = list(set(sp_list) ^ set(pkglist))
    pkg_list.append(latest_sp_name)
    
    return pkg_list

def check_if_upgrade_is_blocked_by_smart_licensing(v1_version, v2_version):
    cmd = "/pkg/bin/smart_lic_sia {} {}".format(v1_version, v2_version)
    status,output = commands.getstatusoutput(cmd)
    if not status:
        logger.debug("Upgrade is allowed by Smart Licensing")
    else:
        logger.debug(status)
        if not output:
           output = "Device upgrade is not allowed due to SIA Out of Compliance"
    return (status, output)

def check_platform_supports_optim_wf (platform):
    allow_optim_workflows_plat = [ "ncs5500", "asr9k", "ncs1k", "ncs1004", "ncs5k", "ncs560", "ncs540", "iosxrwbd", "xrv9k" ]
    if platform not in allow_optim_workflows_plat:
        return False
    else:
        return True

def check_if_allow_optimized_workflow (pkgstate, replace=False, pkg_act_list=None):
    if not check_platform_supports_optim_wf (pkgstate.platform):
        return False
    mini_in_input = False
    if not replace:
        new_pkglist_act = sanitize_superseded_list([x.name_on_router for x in pkgstate.to_activate])
        new_pkgstate_act_list = [x for x in pkgstate.to_activate if x.name_on_router in new_pkglist_act]
        pkgstate.to_activate = new_pkgstate_act_list
        pkg_list_act = set([x.name_on_router for x in pkgstate.to_activate])
    else:
        pkg_list_act = pkg_act_list

    for x in pkg_list_act:
        if '-mini-' in x or '-golden' in x:
            mini_in_input = True
            break

    if mini_in_input:
        for x in pkg_list_act:
            if check_if_sp (x):
                mini_in_input = False
                break
        
    if mini_in_input:
        return True
    return False

def check_if_system_is_committed():
    cmd = "/pkg/bin/sdr_actioncmd  \
                '/opt/cisco/calvados/bin/install-functions.py  \
                                check_if_system_committed'"
    try:
        output = commands.getoutput(cmd)
        logger.debug("check_if_system_is_committed output {}".format(output))
        assert(int(output) == 0 or int(output) == 1)
        output = True if int(output) == 0 else False 
        return output  
    except:
        logger.error("Failed to check if system is in committed state")
        logger.error("Cannot proceed with the operation")
        logger.error("Please try after sometime")
        exit_with_errors()

def check_if_inactive_pkgs_found():
    no_xr_inact_pkgs = False
    no_admin_inact_pkgs = False
    cmd = "sdr_instcmd show install inactive summary"
    try:
        status,output = commands.getstatusoutput(cmd)
        if output:
            lines = output.split("\n\t\n")[0].splitlines()
            for line in lines:
                if no_xr_inactive_pkgs_re.search(line) :
                    no_xr_inact_pkgs = True
                    logger.debug("INFO: XR has NO inactive pkgs")
            if (no_xr_inact_pkgs == False) :
                logger.debug("INFO: XR has inactive pkgs")
    except:
         logger.error ("Error while getting XR inactive pkgs")
         exit_with_errors()

    cmd = "show install inactive summary"
    cmd = get_cal_cmd(cmd)
    try:
        status,output = commands.getstatusoutput(cmd)
        if not status:
            if output :
                lines = output.split("\n\t\n")[0].splitlines()
                for line in lines :
                    if no_admin_inactive_pkgs_re.search(line) :
                        logger.debug("INFO: No admin inactive pkgs")
                        no_admin_inact_pkgs = True
                if no_admin_inact_pkgs == False:
                    logger.debug("INFO: Admin has inactive pkgs")
    except:
        logger.error("Error while getting sysadmin inactive pkgs")
        exit_with_errors()
    if no_admin_inact_pkgs and no_xr_inact_pkgs :
        return (0)
    else :
        return (1)

def log_file_exist():
    inst_oper_file = "install_operation_%d.log" %new_oid
    log_file = os.path.join(INSTALL_DB_LOG_DIR, inst_oper_file)
    if os.path.exists(log_file):
        return True
    return False

def signal_handler_bridge (sig, frame):
    logger.info('CTRL+C was pressed by user!')
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    raise BridgeSMUInstallError
    sys.exit(0)

def signal_handler(sig, frame):
    logger.info('CTRL+C was pressed by user!')
    if not log_file_exist():
        update_operation_id(options.in_opid, update_opid=DECR)
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    sys.exit(0)

def signal_handler_terminate(sig, frame):
    logger.info('User terminated the operation!')
    signal.signal(signal.SIGUSR1, signal.SIG_IGN)
    # Request Sysadmin to cleanup any orchestrator instance                          
    try:     
        logger.info("Cleaning up.. This may take a while, please wait.\n")
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(8)     
        logger.debug(cmd)     
        run_cmd(cmd)     
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(9)
        logger.debug(cmd)
        run_cmd(cmd)
    except:     
        logger.debug("An internal error occurred while requeting Sysadmin to cleanup stale orchestrator instance")
    finally:
        sys.exit(0)

def check_node_sw_inactive(nodeid):
    '''
     we can see node state is OPERATIONAL in 'show sdr'
     but still node may not XR RUN, confirm using
     show platform is node SW_INACTIVE?
    '''
    cmd="show_platform_sysdb -f %s | grep -v -e ERROR: -e State -e -- | tail -1"%(nodeid)
    try:
      status, output = commands.getstatusoutput(cmd)
      if not status and len(output)>1:
         line=re.sub('\s+', ' ',output).strip()
         if any (state in line for state in ["IOS XR RUN",\
                  "OPERATIONAL", "OK", "POWERED_OFF", "UP", "FAILED"]):
            return True
         else:
            return False
      else:
         return True
    except:
         return True

    
def poll_nodes_ready_status (participants, is_force):
    '''
    Poll participant list for READY operation.
    Return True if all participants are ready.
    '''
    i = 60
    any_participant_stuck = False
    new_participants = list (participants)
    while i != 0:
        node_status, failed_nodes =  get_node_ready_status()
        if not node_status:
            if participants[0] == ALL_NODES:
                # Case where participants during retrigger could not
                # be read from marker file.
                logger.debug ("Waiting for all nodes to get ready.")
                time.sleep (10)
                i -= 1
                any_participant_stuck = True
            else:
                for node in participants:
                    if any (node.startswith(x) for x in failed_nodes):
                        logger.debug ("Waiting on node {} getting ready.".format (node))
                        # Going with sleep of 10 s before checking node
                        # availability.
                        # TBD: Work on an infra to trigger an event which could
                        # be used to signify all nodes UP in participant list.
                        # Will remove sleep then.
                        time.sleep (10)
                        i -= 1
                        any_participant_stuck = True
                        break
            if any_participant_stuck:
                continue
            else:
                logger.debug ("No participant is stuck. Continue with install operation.")
                break
        else:
            any_participant_stuck = False
            break

    if any_participant_stuck:
        if is_force:
            node_status, failed_nodes =  get_node_ready_status()
            if not node_status:
                for node in failed_nodes:
                    if ("RP" in node or "RSP" in node):
                        logger.debug ("ERROR: System is not ready for install operation,"
                                " please check both RP nodes are up.")
                        any_participant_stuck = True
                        break
                    else:
                        logger.debug ("INFO: Marking {} as non-participant".format (node))
                        new_participants.remove (node)
                        any_participant_stuck = False    
            
    return not any_participant_stuck, new_participants

def get_node_ready_status():
   '''
    Check all nodes are in either IOS XR RUN or OPERATIONAL or OK
    or UP (and ignore "POWERED_OFF", "FAILED" as these could be admin
    decision) state before starting install operation.
   '''
   nodes_ready = True
   nodes_list = []

   # For now bypassing ncs1k platform, BossHogg Filler cards
   # are having wrong state as PRESENT, once they fix it
   # we can re-enable it
   platform = platN 
   if platform.startswith('ncs1'):
      logger.debug("Skipp Node status check for ncs1k platforms")
      return nodes_ready, nodes_list

   cmd="/pkg/bin/show_sdr_sysdb -n | grep -v -e Slice -e NodeState -e -- "
   try:
      status, output = commands.getstatusoutput(cmd)
      if not status:
       for line in output.split('\n'):
          line=re.sub('\s+', ' ',line).strip()
          if any (state in line for state in ["IOS XR RUN", "UNKNOWN", \
                  "OPERATIONAL", "OK", "POWERED_OFF", "UP", "FAILED"]):
             if any (state in line for state in ["OPERATIONAL", "IOS XR RUN"]):
                ready=check_node_sw_inactive(line.split(' ')[1])
                if ready == False:
                   nodes_ready = False
                   nodes_list.append(line.split(' ')[1])
                   logger.info("Node %s is not ready for install operation"%(line.split(' ')[1]))
          else:
            nodes_ready = False
            nodes_list.append(line.split(' ')[1])
            logger.info("Node %s is not ready for install operation"%(line.split(' ')[1]))
      else:
         logger.debug("ERROR: Failed to get Node running status\n");
   except:
         logger.debug("ERROR: Failed to run command to get Node status \n");

   return nodes_ready, nodes_list

def update_single_id_for_edt():   
     ''' update gl.is_single_id for telemetry event '''
     cmd = "/pkg/bin/sdr_actioncmd \'XR_YANG_IS_SINGLE_ID\'"
     status, cmd = commands.getstatusoutput(cmd)
     if status:
         logger.debug("ERROR: command execution is unsuccessfull")
     else:
         logger.debug(cmd)

def declare_install_in_progress_alarm():
    """ Declare the alarm on XR """
    cmd = "/pkg/bin/sdr_actioncmd \'XR_ALARM_DECLARE_INSTALL_IN_PROGRESS\'"
    logger.debug(cmd)
    run_cmd(cmd)
    """ Declare the alarm on Sysadmin """
    cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(15) 
    logger.debug(cmd)
    run_cmd(cmd)
    
def clear_install_in_progress_alarm():
    """ Clear the alarm on XR """
    cmd = "/pkg/bin/sdr_actioncmd \'XR_ALARM_CLEAR_INSTALL_IN_PROGRESS\'"
    logger.debug(cmd)
    run_cmd(cmd)
    """ Clear the alarm on Sysadmin """
    cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(16) 
    logger.debug(cmd)
    run_cmd(cmd)

def initialize_file_cleanup (): 
    cmd = "/pkg/bin/install_exec_sysadmin \"source /opt/cisco/calvados/bin/install-functions.sh ; trigger_file_cleanup\""
    try:
        run_cmd (cmd)
    except:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug(TB)
        logger.debug("Unable to trigger file cleanup. Continuing")
        pass
    return

def uninitialize_file_cleanup (): 
    cmd = "/pkg/bin/install_exec_sysadmin \"source /opt/cisco/calvados/bin/install-functions.sh ; stop_file_cleanup\""
    try:
        run_cmd (cmd)
    except:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug(TB)
        logger.debug("Unable to stop file cleanup. Continuing")
        pass
    return

def convert_prechecks_configs_to_dict(config_str):
    try:
       for item in config_str.split(","):
          metric, state, action = item.split(":")
          precheck_dict[metric] = []
          precheck_dict[metric].append(state)
          precheck_dict[metric].append(action) 
    except:
       exc_info = sys.exc_info()
       TB = traceback.format_exc()
       logger.debug(TB)
       logger.debug("Failed to convert install profile settings to dictionary")
       pass

def load_install_profile_prechecks_configs():
    try:
       cmd = "/pkg/bin/sdr_actioncmd \'INSTALL_PROFILE_GET_CONFIGS_PRECHECKS\'"
       configs = run_cmd(cmd)
       if "disabled" in configs:
           logger.debug(configs)
           logger.debug("Continuing")
       else:
           convert_prechecks_configs_to_dict(configs)
    except:
       exc_info = sys.exc_info()
       TB = traceback.format_exc()
       logger.debug(TB)
       logger.debug("Unable to get install profile prechecks configurations. Continuing")
       pass

def is_metric_enabled_in_install_profile(metric):
    if precheck_dict and metric in precheck_dict.keys() and \
       precheck_dict[metric][METRIC_STATE] == ENABLED:
        return True
    return False

def run_healthcheck_precheck_metrics():
    outcome = "CONTINUE"
    res = ""
    cmd = "/pkg/bin/install-functions.py check_and_run_healthcheck_metrics prechecks"
    try:
       out = run_cmd(cmd)
       if out:
          outcome = out.split('\n')[0]
          res = "\n".join(out.split('\n')[1:])
    except:
       exc_info = sys.exc_info()
       TB = traceback.format_exc()
       logger.debug(TB)
       logger.debug("Failed to run healthcheck metrics during prechecks phase. Continue on best effort basis")
       pass
    return outcome, res
        
def check_if_default_src_path(pkg=''):
    if options.repository == pkg or \
        (not options.repository.startswith("/") and \
        os.access(INST_MIRROR_PATH + "/" + options.repository, os.R_OK)):
        return True 
    return False

def update_repo_with_default_path(pkg=''):
    if options.repository == pkg: 
       modified_repo = INST_MIRROR_PATH
    else:
       modified_repo = os.path.join(INST_MIRROR_PATH, options.repository)
    options.repository = modified_repo 
    return modified_repo

def main(options, pkglist=[]):
    global add_oid
    global post_precheck_opid

    repo = options.repository if options.repository else None
    iso_in_input = False

    if os.path.isfile (RELOAD_PENDING_MARKER_FILE):
       logger.info("System upgrade abort recovery reload is pending, not starting " \
                   "any install operation till entire system is reloaded")
       exit_with_errors(False)

    # trigger EDT event on start of install operation
    if not options.participants:
        update_telemetry_event()
        create_new = "yes"
        update_parentid_file(new_oid, create_new)

    update_single_id_for_edt()
    ret, oput = check_if_running_op()
    if ret is True:
        if "Autocommit precheck" in oput:
            logger.info("ERROR: %s"%(oput))
        else:
            logger.info("ERROR: There is already an install operation in progress")
        exit_with_errors(False)
    else:
        logger.info("No install operation in progress at this moment")

    if os.path.isfile (PREPCHKPTFILE):
        logger.info("ERROR: Install prepare operation was performed " \
                    "previously. Only \"install prepare clean\" operation " \
                    "or \"install activate\" operation with no package specified " \
                    "is allowed")
        exit_with_errors(False)

    # Check all Nodes are up and running
    logger.info("Checking system is ready for install operation")

    # Run a loop and check for participant status
    if not options.participants:
        node_status, failed_nodes =  get_node_ready_status()
        if (options.force and node_status == False and len(failed_nodes) > 0):
            for node in failed_nodes:
                if ("RP" in node or "RSP" in node):
                    logger.info("ERROR: System is not ready for install operation,"
                            " please check both RP nodes are not up.")
                    exit_with_errors(False)
        elif (node_status == False):
            logger.info("ERROR: System is not ready for install operation,"
                    " please check all nodes are up and in IOS XR RUN state.")
            exit_with_errors(False)
    else:
        # Take into account force.
        unset_op_lock (upd_status_del = False)
        go_ahead, options.participants = poll_nodes_ready_status (options.participants,
                                                                        options.force)
        set_op_lock()
        if not go_ahead:
            logger.error ("Post bridge SMU install, not all participants in {} up. "
                         "Cannot proceed with install replace.".format(
                                              ' '.join(options.participants)))
            logger.error ("ERROR: System is not ready for install operation,"
                    " please check all nodes are up and in IOS XR RUN state.")
            exit_with_errors (False)

    ## Store the current version
    options.from_version = get_version()

    if pkglist :
        pkglist =  list(set(pkglist))

    ## Check the cli usage, and set the right options
    if options.update:
        logger.info("'install update source' in progress")
        logger.info("\nWARNING: 'install update' will be deprecated going forward")
        if options.replace:
            logger.info("         Use 'install replace' instead\n")
        else:
            logger.info("         Use 'install source' instead\n")
    elif options.replace:
        logger.info("'install replace' in progress")
        logger.debug("Repository = %s" %(options.repository))
        if (len(pkglist) > 1):
            logger.error("ERROR: Please provide Golden-ISO as the only argument")
            exit_with_errors(False)
        if "/" in pkglist[0] :
            (actual_repo, name_of_giso) = pkglist[0].rsplit('/', 1)
        else :
            actual_repo, name_of_giso = options.repository, pkglist[0]
        logger.debug("INFO: Actual_repo = %s" %(actual_repo))
        logger.debug("INFO: name_of_giso = %s" %(name_of_giso))
        pkglist = []
        pkglist.append(name_of_giso)
        options.repository = actual_repo
    elif options.prepoptim:
        logger.info("'install prepare' in progress")
    elif options.actioptim:
        logger.info("'install activate' in progress")
    else:
        logger.info("'install source' in progress")

    options.update = True 

    if options.commit:
        if options.replace == False:
            logger.info("'commit' keyword is only supported with install replace")
            exit_with_errors(False)
        else:
            logger.info("Install replace with commit option is triggered")   
    
    if options.force:
        if options.replace == False:
            logger.info("'force' keyword is only supported with install replace")
            exit_with_errors(False)
        else:
            logger.info("Install replace with force option is triggered")   
 
    ## If 'replace' is used, ensure GISO is used in input
    giso_found = False
    if options.replace:
        for pkg in pkglist:
            if "-golden" in pkg:
                if ".iso" in pkg: 
                    logger.debug("replace option used. GISO found: %s" %(pkg))
                    giso_found = True
                else:
                    if options.prepoptim  or options.actioptim:
                        giso_found = True
                    else:
                        logger.error("ERROR: Golden-ISO file's extension is incorrect!!!")
                        exit_with_errors(False)
            if check_if_sp(pkg):
                logger.error("ERROR: Service Pack can not be used with 'replace' option")
                logger.error("Service Pack given: %s" %(pkg))
                exit_with_errors(False)
                
        if giso_found == False:
            logger.info("ERROR: Golden-ISO has to be used when 'replace' option is used")
            exit_with_errors(False)
    sp_list = []
    for pkg in pkglist:
        if "*" in pkg:
            if options.update:
                logger.error("Error: Install update doesn't support wildcard(s).")
            if options.upgrade:
                logger.error("Error: Install upgrade doesn't support wildcard(s).")
            exit_with_errors(False)
        if check_if_sp(pkg):
            sp_list.append(pkg)
        elif ".iso" in pkg:
            if not iso_in_input:
                iso_in_input = True
            else:
                logger.error("Error: Multiple ISOs in input list not supported.")
                exit_with_errors(False)

            options.update = False
            options.upgrade = True
            options.to_version = validate_iso(pkg)
            if not options.to_version:
                logger.error("Error: iso %s cannot be parsed as per "
                             "current naming convention"%pkg)
                exit_with_errors(False)
            logger.info("ISO %s in input package list. "
                        "Going to upgrade the system to version %s."%(pkg, options.to_version))
        elif (options.prepoptim  or options.actioptim) and ('-mini-' in pkg or '-golden' in pkg):
            if not iso_in_input:
                iso_in_input = True
            else:
                logger.error("Error: Multiple ISOs in input list not supported.")
                exit_with_errors(False)

            options.update = False
            options.upgrade = True
            options.to_version = validate_iso_prepare(pkg)
            if not options.to_version:
                logger.error("Error: iso %s cannot be parsed as per "
                             "current naming convention"%pkg)
                exit_with_errors(False)
            logger.info("ISO %s in input package list. "
                        "Going to upgrade the system to version %s."%(pkg, options.to_version))

    if is_new_label_format is True:
        logger.debug("is_new_label_format = True")
    else:
        logger.debug("is_new_label_format = False")

    if (iso_in_input and options.from_version != options.to_version):
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(20)
        status,output = commands.getstatusoutput(cmd)
        if not status:
            logger.debug("anti-theft api output: %s" %output)
            if output == "ENABLED":
                logger.error("ERROR: Re-image protection is enabled...")
                logger.error("ERROR: hence SU operation is blocked, Exiting install operation!\n")
                exit_with_errors(False)

    ## Check if the system is in committed state
    if options.replace or options.upgrade:
        is_upg_blocked, error_message = check_if_upgrade_is_blocked_by_smart_licensing(options.from_version, options.to_version) 
        if is_upg_blocked:
            logger.error(error_message)
            exit_with_errors(False)

        # Unlock the lock grabbed for replace with forcr ONLY
        # This is needed as we are going to call commit and remove
        # using sdr_instcmd (C code)    
        if options.force and not options.participants:
            autorecover_marker_file="/var/log/install/instdb/force_precheck_in_prog"
            fd = open(autorecover_marker_file, "w") 
            fd.close()
            logger.debug('force_precheck_in_prog file created') 
                
            try: 
                cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(5)
                logger.debug(cmd)
                run_cmd(cmd)
                logger.debug ("Successfully unset op_in_progress globals")
            except:
                logger.debug ("Error encountered while trying to unset op_in_progress globals, retrying..")
                run_cmd(cmd)

        is_committed = check_if_system_is_committed()
        if is_committed:
            logger.debug("System is in committed state")
        elif options.participants:
            logger.debug ("Bridge SMUs installed. Continue with install operation.")
        else:
            # Invoke autocommit.py to perform install commit
            if options.force: 
                logger.info("INFO: The system is not in committed state. Triggering 'install commit'")
                try:
                    update_single_id_for_edt()
                    cmd = "/pkg/bin/autocommit.py"
                    logger.debug(cmd)
                    comm_status = run_cmd(cmd)
                    logger.debug("Commit return status: %d" %(int(comm_status)))
                    if int(comm_status) == 0:
                        logger.debug("Precheck commit successful")
                    elif int(comm_status) == -1:
                        logger.error("Triggerring 'install commit' during precheck failed")
                        exit_with_errors(False)
                    elif int(comm_status) > 0 and int(comm_status) <= 5:
                        logger.error ("'install commit' failed on retries")

                        # Latch the operation id after commit and remove has completed
                        # This is needed as operation_id.txt gets over-written by replace
                        # op id to have the same id for prepare and activate
                        # Once replace completes either with success or failure, latest
                        # operationid has to written back to the operation_id.txt
                        # so that operation following replace picks proper (subsequent)
                        # operation id 
                        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                        oidfile = open(op_id_file, 'r+')
                        post_precheck_opid = oidfile.read()
                        oidfile.close()
                        logger.debug("post_precheck_opid : %s" %(post_precheck_opid))

                        exit_with_errors(False)
                except:
                    op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                    oidfile = open(op_id_file, 'r+')
                    post_precheck_opid = oidfile.read()
                    oidfile.close()
                    logger.debug("post_precheck_opid : %s" %(post_precheck_opid))

                    logger.error ("Error while issuing 'install commit'")
                    exit_with_errors(False)

            else:
                logger.error("ERROR: The system is not in committed state. Please issue 'install commit' and retry this operation")
                exit_with_errors(False)

        # Invoke autoremove_inact_all.py to perform install remove inactive all
        if options.force and not options.participants:
            call_remove  = check_if_inactive_pkgs_found()
            if (call_remove == 0) :
                logger.debug("No inactive packages found")
            else :
                logger.info("INFO: Inactive packages found, Triggering 'install remove inactive all'")
                try:
                    update_single_id_for_edt()
                    cmd = "/pkg/bin/autoremove_inact_all.py"
                    logger.debug(cmd)
                    rem_status = run_cmd(cmd)
                    logger.debug("Remove return status: %d" %(int(rem_status)))
                    if int(rem_status) == 0:
                        logger.debug("Precheck remove successful")
                    elif int(rem_status) == -1:
                        logger.error("Triggerring 'install remove inactive all' during precheck failed")
                        exit_with_errors()
                    elif int(rem_status) > 0 and int(rem_status) <= 3:
                        logger.error ("'install remove inactive all' failed on retries")

                        # Latch the operation id after commit and remove has completed
                        # This is needed as operation_id.txt gets over-written by replace
                        # op id to have the same id for prepare and activate
                        # Once replace completes either with success or failure, latest
                        # operationid has to written back to the operation_id.txt
                        # so that operation following replace picks proper (subsequent)
                        # operation id 
                        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                        oidfile = open(op_id_file, 'r+')
                        post_precheck_opid = oidfile.read()
                        oidfile.close()
                        logger.debug("post_precheck_opid : %s" %(post_precheck_opid))

                        exit_with_errors()
                except:
                    logger.error ("Error while issuing 'install remove insactive all'")
                    op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                    oidfile = open(op_id_file, 'r+')
                    post_precheck_opid = oidfile.read()
                    oidfile.close()
                    logger.debug("post_precheck_opid : %s" %(post_precheck_opid))
                    exit_with_errors()
               
            if os.path.exists("/var/log/install/instdb/force_precheck_in_prog"):
                os.remove("/var/log/install/instdb/force_precheck_in_prog")

            # Latch the operation id after commit and remove has completed
            # This is needed as operation_id.txt gets over-written by replace
            # op id to have the same id for prepare and activate
            # Once replace completes either with success or failure, latest
            # operationid has to written back to the operation_id.txt
            # so that operation following replace picks proper (subsequent)
            # operation id 
            op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
            oidfile = open(op_id_file, 'r+')
            post_precheck_opid = oidfile.read()
            oidfile.close()
            logger.debug("post_precheck_opid : %s" %(post_precheck_opid))

            # Grab the lock back for the replace operation as we are done with
            # prechecks 
            try:
                cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(4)
                logger.debug(cmd)
                status,output = commands.getstatusoutput(cmd)
                if not status:
                   if oper_none_re.search(output):
                      logger.debug("No install operation in progress on Sysadmin, proceeding..")
                   elif oper_oip_re.search(output):
                      print(output)
                      exit_with_errors() 
                   elif oper_nodestatus_re.search(output):
                      print(output)
                      exit_with_errors()
                   else:
                      #This is either internal error or WTBA found case, print the message as is
                      print(output)
                      unset_op_lock()
                      exit_with_errors()
            except:
                logger.info ("An internal error occured while trying to acquire operation lock")
                unset_op_lock()
                exit_with_errors() 

    try: 
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(19)
        logger.debug(cmd)
        run_cmd(cmd)
        logger.debug("INFO: Copied yml to all nodes")
    except:
        logger.debug ("Error copying the yml files on sysadmin")

    if len(sp_list)>0:
        pkglist = process_sp_list(sp_list,pkglist, \
                                    options.from_version,options.to_version)
    options.userpkgs = pkglist[:]
    if repo and repo.split(":")[0] not in REPO_PROTO and not repo[0] == '/' :
        is_default = False
        if options.replace:
           is_default = check_if_default_src_path(pkglist[0])
           if is_default == True:
              repo = update_repo_with_default_path(pkglist[0])
        if is_default == False:
           logger.error("Error: %s is invalid" % (repo.split(":")[0]))
           logger.error("1. Please check if the repository protocol used is supported\n" \
                        "2. Check if the ISO exists in the default path: /harddisk:/mirror")
           exit_with_errors()

    # Run Healthcheck tool if it is enabled
    if options.replace and options.commit and not options.force \
           and platN in PRE_POSTCHECKS_SUPPORTED_PLATFORMS:
       outcome, res = run_healthcheck_precheck_metrics()
       if res:
          logger.info(res)
       if outcome == "STOP": 
          raise RuntimeError("Error: Healthcheck prechecks failed")

    if options.force:
        print "********************************************************************************\n" \
              "                                   WARNING !!!                                  \n" \
              "    With 'force' option:                                                        \n" \
              "i.  Auto-recovery is enabled. In the scenario of auto-recovery of any node(s)\n" \
              "    debuggability data will be lost if the node(s) are re-imaged/reloaded for \n" \
              "    auto-recovery\n" \
              "ii. Upgrade matrix check is not enforced.\n" \
              "    In the scenario of an unsupported upgrade/downgrade, the install operation\n" \
              "    may result in errors.\n" \
              "********************************************************************************\n"
    if options.commit:
       print "\nWARNING: Commit option is used. "\
             "This will auto commit the software after the install activation completes successfully.\n"\
             "However, if any login failures are seen after the reload, "\
              "rollback would be possible with USB/PXE boot or appropriate workaround.\n"
       user_str = "Do you want to continue?\n[yes:no]:[yes] "
       if not options.noprompt:
           response = '' 
           signal.alarm(TIMEOUT_YESNO)
           while True:
               response = raw_input(user_str)
               if response.lower() == "no" :
                   logger.error("Error: User aborted the operation")
                   exit_with_errors() 
               elif response.lower() == "yes" or len(response) == 0:
                   break
               else:
                   logger.info("Invalid: %s. Enter 'Yes' or 'No'" %(response))
                   continue
           signal.alarm(0)
    if options.update:
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(12)
        status,output = commands.getstatusoutput(cmd)
        if not status:
            logger.debug("su_op_type: %s" %output)
            if output == "SU_OPERATION_DONE":
                logger.error("ERROR: SU/ISSU operation is performed on system...")
                logger.error("ERROR: The system is not in committed state. Please issue 'install commit' and retry this operation")
                exit_with_errors()
       
        update_packages(options, pkglist)
    elif options.prepoptim or options.actioptim:
        upd_message = "User %s, Op Id %s"%(os.getenv('AAA_USER'), str(new_oid))
        if not options.actioptim:
            upd_message += "\ninstall prepare"
            options.noprompt = True
        else:
            upd_message += "\ninstall activate"
        try:
            update_status_optim_prep (init = True)
            update_status_optim_prep (message = upd_message)
            declare_install_in_progress_alarm()
            create_activate_env(None, True, pkglist, xractive_pkgs)
            if options.actioptim:
                optimised_activate ()
            else:
                if options.background:
                    logger.addHandler(ch)
                logger.info ("Install operation %d finished successfully"%(new_oid))
                '''
                   Stream telemetry data for optimised Install Prepare cli 
                   operation status.
                   install prepare <pkg name>
                   install prepare id <xx>
                ''' 
                if (options.prepoptim == True and options.actioptim == False):
                    update_telemetry_event() 

                # For the sake of having similar messaging on console, print with tag.
                if options.background:
                    tzname = time.tzname[0]
                    pid = os.getpid ()
                    tag = "%INSTALL-INSTMGR-2-OPERATION_SUCCESS"
                    msg = "Install operation %d finished successfully"%(new_oid)
                    message = "%s: install[%s]: %s : %s"%(tzname, pid, tag, msg)
                    # Add an entry for show logging.
                    log_logger = "%s -s info %s"%("/pkg/bin/logger", message)
                    run_cmd (log_logger)
            update_status_optim_prep (init = True)
        except:
            if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")

            bErrFound = False
            if os.path.isfile (UPDATE_STATUS_FILE_CALV):
                with open(UPDATE_STATUS_FILE_CALV, 'r') as fIn:
                    mdata_dict = json.load(fIn)
                    if 'Errors' in mdata_dict and len(mdata_dict['Errors']) > 0 or \
                                mdata_dict.has_key ("ErrGeneric"):
                        logger.error('Following error(s) occurred in sysadmin vm(s) during install operation:')
                        if mdata_dict.has_key ("ErrGeneric"):
                            flagIPs = mdata_dict["ErrGeneric"].split(',')
                            if all(re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i) for i in flagIPs):
                               for ip in flagIPs:
                                   card = get_card_from_cal_ip(ip)
                                   logger.error ("Preparation failed on %s"%(card))
                            else:
                               logger.error ("Preparation failed with the error:\n%s"%(mdata_dict["ErrGeneric"]))
                        bErrFound = True
                        ErrNo   = 0
                        ErrList = []
                        ErrMsg  = ""
                        if mdata_dict.has_key ('Errors'):
                            for node_ip in mdata_dict['Errors']:
                                for line in mdata_dict['Errors'][node_ip]:
                                    ErrNo = ErrNo + 1
                                    new_err='#%d %s'%(ErrNo, line)
                                    ErrList.append(new_err)
                                err_msg = '\n\t\t'.join(ErrList)
                                ErrList = [] # reset list
                                ErrNo = 0 # reset for next node.
                                card = get_card_from_cal_ip(node_ip)
                               
                                ErrMsg = ErrMsg + ("""
                Error stack for %s : 
                   %s
                                               """%(card, err_msg))
                        logger.error("""
                   %s
              Please collect 'show tech-support install one-showtech' from XR and 
              'show tech-support ctrace' from Admin and pass this information to 
              your TAC representative for support. 
                                     """%(ErrMsg))
            if not bErrFound:
                logger.error("""
                    Error: An exception is hit while waiting for optimised operation to complete.
                    If you hit the same error on retries, please collect 'show tech-support install one-showtech'
                    from XR and 'show tech-support ctrace' from Admin and pass this information
                    to your TAC representative for support.
                            """)
            handle_abort_optim_workflow ()
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
 
            exit_with_errors()
    elif options.upgrade:
        clear_staging_root ()
        update_status_optim_prep (init = True)
        upd_message = "User %s, Op Id %s"%(os.getenv('AAA_USER'), str(new_oid))
        upd_message += "\ninstall source pre-checks and fetching in progress."
        update_status_optim_prep (message = upd_message)
        upgrade_packages(options, pkglist)
    else:
        logger.error("Invalid operation")
        exit_with_errors()

def update_packages(options, pkgs):
    """ Update the packages with patch or features """
    repo = options.repository
    cur_version = get_version()
    cur_release = 'r' + ''.join(cur_version.split('.'))

    if options.to_version:
        logger.info('Version parameter is ignored with "update" option\n')
    if repo[:4] == 'tftp' and not pkgs:
        logger.error('Package list is required if TFTP repository is used\n')
        exit_with_errors()

    update(options, cur_version, pkgs)
    return 0


def update(options, version, pkgs):
    """ Update SMUs or optional packages of same version """
    dest_dir = os.path.join(TMP_ROOT, TMP_PATH, TMP_DIR, version)
    logger.info("Update in progress...")
    output = ''

    with PkgDownload(options, version, dest_dir) as pkgdownload :
        pkgstate = package.PackageInfo(options, pkgdownload.v1, act_cmd_op, inact_cmd_op, platN, True)
        pkgstate.tmpstage = dest_dir
        pkgdownload.pkgstate = pkgstate
        if pkgdownload.scheme == 'tftp':
            logger.info("""
            Auto dependency management is not possible with TFTP repository.
            Given set of packages will be downloaded and installed if
            dependencies are met. Only full rpm names should be used with TFTP.
            """)

        else :
            # If no package specified , list all SMU , else packaging matching
            pkgstate.input_package = pkgdownload.list(options, pkgs)
            pkgstate.repo_package = pkgdownload.repopkgs
            pkgstate.filter()
            while pkgstate.to_download_package:
                pkgstate.to_download_package = pkgstate.sanitize_remove_duplicates(pkgstate.to_download_package)
                pkgstate.to_download_package = \
                                    uniq_package(pkgstate.to_download_package)
                dnld=pkgdownload.not_in_localrepo(pkgstate.to_download_package)
                if dnld : 
                    dnld = pkgdownload.add_tp_base(dnld)
                    pkgdownload.get(dnld)
                pkg_downloaded = pkgstate.to_download_package[:]
                ''' 
                if any sp is there in input then filter the list of SMUs
                specified in the CLI, remove the SMUs which are there in SP
                '''
                pkgstate.rpms_in_bundle = []
                for pkg in pkgstate.to_download_package:
                    if check_if_sp(pkg.filename):
                        sp_name = pkg.filename
                        iso_path = os.path.join(pkgstate.tmpstage,pkg.filename)
                        pkgstate.bundle_iso_get_rpms(iso_path)
                        break

                if pkgstate.rpms_in_bundle:
                    for rpm in pkgstate.rpms_in_bundle:
                        for pkg1 in pkgstate.to_download_package:
                            if pkg1.filename == rpm:
                                logger.info("Skipping %s as this is part of "
                                            " Service Pack %s" %(pkg1.filename,
                                                sp_name))
                                pkg_downloaded.remove(pkg1)

                pkgstate.to_download_package = pkg_downloaded

                pkgstate.downloaded_package.extend(pkgstate.to_download_package)
                # Download was success to remove from list of to_download
                pkgstate.to_download_package = []
                # Check compatibility , there might be need of fetching more
                #more_package = pkgstate.checkcompat()
                if pkgstate.update_giso or pkgstate.upgrade_giso:
                    # If its GISO the package to be activated is all packages in 
                    # GISO minus the active packages, no compatibility check to  
                    # be done
                    pkgstate.to_activate = uniq_package(pkgstate.downloaded_package[:])
                    more_package = []
                else :
                    pkgstate.checkcompat()
                more_package = [ p.filename for p in pkgstate.to_download_package] 
                #Ignore if already downloaded
                npackages = more_package[:]
                for np in npackages:
                    for dp in pkgstate.downloaded_package:
                        if np == dp.filename and np in more_package:
                            more_package.remove(np)
                more_packages = pkgdownload.not_on_box(more_package)
                if more_package:
                    pkgstate.input_package = pkgdownload.getpkgs(options, more_package)
                    pkgstate.filter()
        if not pkgdownload.scheme == 'tftp':
            pkgstate.to_add = list(set(pkgstate.downloaded_package))
    
        return add_activate(pkgstate, pkgdownload)

def uniq_package(pkglist):
    seen_pkg = []
    package = []
    for pkg in pkglist :
        if pkg and pkg.filename not in seen_pkg :
            package.append(pkg)
            seen_pkg.append(pkg.filename)
    return package

def split_add_operation(cmd, bSync):
    """ Prepare multiple install add command """
    fixed = ' '.join(cmd.split(" ")[:5])
    packages = cmd.split(" ")[5:]
    slices = []
    slice = fixed
    for p in packages:
        if (len(slice) + len(p)) > MAX_CLI_LEN :
            if bSync:
                slice += " synchronous "
            slices.append(slice)
            slice = "%s %s"%(fixed,p)
        else :
            slice = "%s %s"%(slice,p)
    if len(slice) > len(fixed) :
        if bSync:
            slice += " synchronous "
        slices.append(slice)
    return slices

def add(single_add_cmd, bSync, pkgdownload):
    """ Add the software packages, if number of CLI grows more than 1000 split
    in multiple CLIs and add them """
    add_ids = []
    output = '' 
    global add_oid
    if len(single_add_cmd) > MAX_CLI_LEN :
        add_cmds = split_add_operation(single_add_cmd, bSync)
    else:
        if bSync:
            single_add_cmd += " synchronous "
        add_cmds = [single_add_cmd]
    for add_cmd in add_cmds:
        try :
            opp_id = get_latest_id()
            if opp_id:
                opp_id = int(opp_id) + 1
                update_parentid_file(opp_id)
            update_single_id_for_edt()
            cmd = add_cmd.split(" ")
            add_cmd = [x for x in cmd if x] #Imp else instmgr crashes
            proc = subprocess.Popen(add_cmd,stdout=subprocess.PIPE)
            proc.wait()
            output = proc.stdout.read()
        except :
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("""
                Error: An exception is hit while waiting for add to complete.
                Execute "show install request" to check for completion of install add.
                After completion of install add execute "show install repository" to check
                the packages added. Execute "install activate id <add id> to proceed to
                activate operation.
                If you hit same error on retries, please collect "show tech install"
                and contact cisco-support.
                """)
            logger.error(output)
            pkgdownload.clear_staging()
            exit_with_errors()
        finally:
            pkgdownload.clear_staging()

        id_re = oper_id_re.search(output)
        if id_re :
            op_id = id_re.group(1)
        elif not id_re and len(add_cmds) > 1 :
            logger.error("Failed to get operation ID for install add, Please retry")
            logger.error(output)
            exit_with_errors()

        if op_id and op_id > add_oid:
            add_oid = op_id

        if oper_bg_re.search(output) and oper_success_re.search(output):
            logger.debug(output)
            # dont delete/change below logging. Used for yang log parsing
            logger.info("Install add operation successful")
            add_ids.append(op_id)
        elif oper_bg_re.search(output) and not oper_success_re.search(output):
            # If operation went in backgrount wait for it to complete
            logger.info("Operation going on in background")
            in_progress = True
            cmd = ['sdr_instcmd', 'show', 'install', 'request']
    
            while in_progress :
                try :
                    proc = subprocess.Popen(cmd,stdout=subprocess.PIPE)
                    proc.wait()
                    output1 = proc.stdout.read()
                except :
                    exc_info = sys.exc_info()
                    TB = traceback.format_exc()
                    logger.debug(TB)
                    logger.error("show install request failed.")
                    logger.error(output1)
                if oper_none_re.search(output1):
                    in_progress = False
    
            # Test add status 
            cmd = ['sdr_instcmd', 'show', 'install', 'log', op_id]
            try :
                proc = subprocess.Popen(cmd,stdout=subprocess.PIPE)
                proc.wait()
                output2 = proc.stdout.read()
            except:
                exc_info = sys.exc_info()
                TB = traceback.format_exc()
                logger.debug(TB)
                logger.error(output2)
                # dont delete/change below logging. Used for yang log parsing
                logger.error("Install add operation failed.")
                return -1
            if oper_success_re.search(output2):
                logger.info(output2)
                # dont delete/change below logging. Used for yang log parsing
                logger.info("Install add operation successful")
                add_ids.append(op_id)
            else:
                logger.error(output2)
                # dont delete/change below logging. Used for yang log parsing
                logger.error("Install add failed")
                exit_with_errors()
        elif not oper_success_re.search(output):
            logger.error("Install add failed")
            logger.error("Error in ADD phase: Please check \'show install log %s detail\' for error details" %(add_oid))
            exit_with_errors()
    logger.debug("Waiting for 5 sec to install add cleanup")
    time.sleep(5)
    return add_ids

def run_cmd (cmd):
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                               stderr=subprocess.PIPE, shell=True)
    out, error = process.communicate()
    sprc = process.returncode
    if sprc is None or sprc != 0:
        out = error
        raise RuntimeError("Error CMD=%s returned --->%s" % (cmd, out))
    else:
        logger.debug ("CMD=%s OUTPUT=%s"%(cmd, out))
        pass
    return out.strip()

'''
    Best effort to start with emptying the staging root.
'''
def clear_staging_root ():
    dest_dir = os.path.join(TMP_ROOT, TMP_PATH, TMP_DIR)
    try:
        for (root, dirs, files) in os.walk(dest_dir):
            for f in files:
                os.unlink(os.path.join(root, f))
            for d in dirs:
                if os.path.ismount (os.path.join(root, d)):
                    cmd = "umount -R %s"%(os.path.join(root, d))
                    run_cmd (cmd)
                if os.path.islink (os.path.join(root, d)):
                    os.unlink (os.path.join(root, d))
                else:
                    shutil.rmtree(os.path.join(root, d))
    except:
        pass

def update_status_optim_prep (init = False, message = None):
    if init:
        if os.path.isfile (UPDATE_STATUS_FILE_XR):
            os.remove (UPDATE_STATUS_FILE_XR)
        if os.path.isfile (UPDATE_STATUS_FILE_CALV):
            os.remove (UPDATE_STATUS_FILE_CALV)
        if os.path.isfile (VM_INFO_FILE):
            os.remove (VM_INFO_FILE)
    if message:
        with open(UPDATE_STATUS_FILE_XR, 'a') as fd:
            fd.write ("%s"%(message))
    return

def copy_matrix_files_to_sysadmin():
    # Copy V1 matrix files to sysadmin from /pkg/bin/upgrade_matrix or stub file if present
    if os.path.isfile("/tmp/inst_v1_matrix_stub.txt"):
        cmd1 = 'mkdir -p %s ; sync ; scp %s://%s %s'%(STAGING_COMPAT_MATRIX_DIR, lead_rp_vm, V1_STUB_COMPAT_MATRIX_FILE, STAGING_COMPAT_MATRIX_DIR)
    else:
        cmd1 = 'mkdir -p %s ; sync;  scp %s://%s/* %s'%(STAGING_COMPAT_MATRIX_DIR, lead_rp_vm, COMPAT_MATRIX_DIR, STAGING_COMPAT_MATRIX_DIR)
    cmd = "/pkg/bin/sdr_actioncmd '%s'"%(cmd1)
    output_str = run_cmd (cmd)
    if output_str == sdr_actioncmd_error:
        raise RuntimeError("Error: Unable to copy V1 matrix files to sysadmin")

    #Copy V2 stub matrix file to sysadmin if present
    if os.path.isfile("/tmp/inst_v2_matrix_stub.txt"):
        cmd1 = 'scp %s://%s %s'%(lead_rp_vm, V2_STUB_COMPAT_MATRIX_FILE, STAGING_COMPAT_MATRIX_DIR)
        cmd = "/pkg/bin/sdr_actioncmd '%s'"%(cmd1)
        output_str = run_cmd (cmd)
        if output_str == sdr_actioncmd_error:
           raise RuntimeError("Error: Unable to copy V2 stub matrix file to sysadmin")
    
def check_if_giso_upgrade_is_allowed(pkg_list_act, action=ERROR_ON_FAILURE):
    giso_in_act_list = filter(lambda x:'-golden' in x, pkg_list_act)
    if len(giso_in_act_list) > 0:
        cal_giso_path = os.path.join("/misc/disk1/tftpboot", giso_in_act_list[0])
        vrf_str = "/opt/cisco/calvados/bin/vrfch.sh CTRL_VRF"
        cmd1 = "%s /opt/cisco/calvados/bin/install-functions.py get_upgrade_matrix_info %s %s %s %s" \
                %(vrf_str, "from-running", UPGRADE_MATRIX_LOGFILE, cal_giso_path, False)
        cmd = "/pkg/bin/sdr_actioncmd '%s'"%(cmd1)
        output_str = run_cmd(cmd)
        if UPGRADE_ALLOWED_MSG not in output_str:
           if action == WARN_ON_FAILURE: 
              logger.info("WARNING: Upgrade matrix check failed. Install profile is configured to warn and continue.") 
           else:
              logger.error("Preparation failed with the error:\n%s\n" %output_str)
              raise Exception(output_str)
                 
def create_bridge_smu_marker_file(op_id, url_str):
    bimfile = open(BRIDGE_SMU_INSTALL_MARKER_FILE, 'w')
    bimfile.write('Opid: '+str(op_id)+'\n')
    bimfile.write('arg: '+url_str.replace("source", "")+'\n')
    bimfile.close()

def check_and_install_bridge_smus (pkg_act_list):
    ''' If bridge smu is required then install bridge smu before proceed next'''
    global post_precheck_opid
    signal.signal(signal.SIGINT, signal_handler_bridge)
    copy_matrix_files_to_sysadmin()

    iso_path=os.path.join("/misc/disk1/tftpboot", pkg_act_list[0])
    cmd = "/pkg/bin/install_exec_sysadmin " \
          "\"/opt/cisco/calvados/bin/install-functions.py get_bridge_smu_info " \
          "%s %s\"" %("/tmp/bridge_smu_info.txt", iso_path)
    output = run_cmd(cmd)
    logger.debug("Bridge smu information = %s" %(output))

    # Add options.participant check for triggering smu install
    if "(not installed)" in output:
        logger.debug("Bridge smu is not installed, lets install\n")
        create_bridge_smu_marker_file(inst_replace_op_id, inst_replace_url_str)
        remote_cmd = "/opt/cisco/calvados/bin/utils/install_add_replicate_brs.py -b " \
                     "%s -r %s -sdr %s" %(iso_path, options.from_version,
                     "default-sdr")
        logger.debug("Bridge SMU add command %s\n"  %(remote_cmd))
        cmd = "/pkg/bin/install_exec_sysadmin \'%s\'" % (remote_cmd)

        output = run_cmd(cmd)
        logger.debug("Returned pkg list and reload type = %s" %(output))
        output=output.replace("\n", "")
        if len(output) :
            logger.info("Bridge smu is available and same will be installed prior to upgrade")
            logger.info ("Triggering bridge smu activate operation.\nThis may take a while...")
            spl_output = output.split()
            # last word will be 0 or 1 ; 0 for non reload and 1 for reload smu
            last_token = spl_output[-1]
            last_prev_token = spl_output[-2]

            if "sdr_instmgr_present" in last_token: 
                is_sdr_instmgr_present = last_token.split(":")[1]
                rm = spl_output[:-1]
            if "reload_smu_present" in last_prev_token: 
                is_reload_smu = last_prev_token.split(":")[1]
                rm = spl_output[:-2]

            pkg_list = ' '.join([str(elem) for elem in rm])

            if is_reload_smu == "1":
                logger.debug("Reload smu is present in the bridge smus")

            if is_sdr_instmgr_present == "1":
                logger.debug("sdr_instmgr binary is present in the bridge smus")

            # Do not delete the status update file.
            upd_message = "\nActivating bridge SMUs packaged in GISO."
            update_status_optim_prep (message = upd_message)
            unset_op_lock(upd_status_del = False)

            if options.force:
                activate_cmd = "/pkg/bin/sdr_instcmd install activate pkg 0x0 " \
                       "%s force noprompt synchronous bridge-smu" % (pkg_list)
            else:
                activate_cmd = "/pkg/bin/sdr_instcmd install activate pkg 0x0 " \
                       "%s noprompt synchronous bridge-smu" % (pkg_list)

            logger.debug("Bridge SMU activate command %s\n"  %(activate_cmd))

            commit_marker_file="/var/log/install/instdb/replace_commit_marker_file.txt"

            try:
                logger.debug ("Update single ID EDT request.")
                update_single_id_for_edt()
                opp_id = get_latest_id()
                if opp_id:
                    opp_id = int(opp_id) + 1
                    update_parentid_file(opp_id)
                output = run_cmd(activate_cmd)
                if oper_success_re.search(output):
                    # dont delete/change below logging. Used for yang log parsing
                    upd_message = "\nBridge SMU install was successful."
                    update_status_optim_prep (message = upd_message)
                    logger.info("Bridge SMU install was successful")
                    # With bridge SMU installed, continue after taking the lock.
                    if instmgr_impact_re.search (output):
                        logger.info("Instmgr process was impacted. Delete commit "
                                                "marker file")
                        is_sdr_instmgr_present = "1"
                    else:
                        is_sdr_instmgr_present = "0"

                    if oper_reboot_re.search (output) or oper_reload_re.search (output):
                        logger.info("Optype is reboot. Delete commit "
                                                "marker file")
                        is_reload_smu = "1"
                    else:
                        is_reload_smu = "0"

                    if is_reload_smu == "1" or is_sdr_instmgr_present == "1":
                        if os.path.isfile (commit_marker_file):
                            os.unlink (commit_marker_file)
                else:
                    upd_message = "\nBridge SMU install failed."
                    update_status_optim_prep (message = upd_message)
                    id_re = oper_id_re.search(output)
                    if id_re :
                        op_id = id_re.group(1)
                        logger.error ("Bridge SMU install failed. Please "
                                "check \'show install log %s detail\' "
                                "for error details" %(op_id))
                    elif not id_re and len(add_cmds) > 1 :
                        logger.error("Failed to get operation ID for "
                                "bridge SMU activate operation")
                        logger.error(output)
                    raise RuntimeError ("Bridge SMU install failed.")
            except:
                raise
            finally:
                op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                oidfile = open(op_id_file, 'r+')
                post_precheck_opid = oidfile.read()
                oidfile.close()
                logger.debug("post_precheck_opid : %s" %(post_precheck_opid))

            with open (BRIDGE_SMU_INSTALL_MARKER_FILE, 'r') as fd:
                logger.debug ("Bridge marker file updated as:")
                logger.debug (fd.read())

            if is_reload_smu == "1" or is_sdr_instmgr_present == "1":
                if is_reload_smu == "1":
                    upd_message = "\nPending reload post bridge SMU installation."
                    update_status_optim_prep (message = upd_message)
                    logger.info ("The system will reload post which install replace "
                                 "workflow will be triggered. Please execute "
                                 "'show install request' to monitor the progress.")
                else:
                    logger.info ("Bridge SMU involves changes to the install infra. "
                                 "Install replace will be retriggered in the background. "
                                 "Please execute 'show install request' to monitor the progress.")
                global rem_on_sucess
                rem_on_sucess = exitworkflow_remfiles
                raise ExitWorkflow
            else:
                set_op_lock ()
        else:
            logger.error("Bridge smu is needed but not available in the given GISO")
    return

def create_activate_env(pkgstate, replace=False, pkg_act_list=None, xractive_pkgs=[]):
    ''' Create config file for parallel prep '''
    ''' Check if notpompt is provided by user, else prompt that
        this operation will result in a reload of the system'''

    user_str = "This install operation will reload the system, continue?\n [yes:no]:[yes] "
    logger.debug("User_str = %s" %(user_str))
    if not options.noprompt:
        response = '' 
        signal.alarm(TIMEOUT_YESNO)
        while True:
            response = raw_input(user_str)
            if response.lower() == "no" :
                logger.info ("User aborted this install operation")
                handle_abort_optim_workflow ()
                exit_with_errors()
            elif response.lower() == "yes" or len(response) == 0:
                break
            else:
                logger.info("Invalid: %s. Enter 'Yes' or 'No'" %(response))
                continue
        signal.alarm(0)
           
    if options.replace:
        try:
            # Compatibility matrix feature will not be supported for xrv9k and iosxrwbd
            platform = get_platform()
            if not os.path.isfile (SKIP_MATRIX_CHECKS) and platform not in MATRIX_UNSUPPORTED_PLATFORMS and not options.skip_upgrade_matrix_checks:
               check_and_install_bridge_smus (pkg_act_list)
               signal.signal(signal.SIGINT, signal_handler)
        except ExitWorkflow:
            raise
        except:
            logger.error("""
                    Error: An exception is hit while waiting for activation of packages
                    to complete. Execute "show install request" to check for any install  
                    operation that is in progress.
                    If you hit same error on retries, please collect "show tech install"
                    and contact cisco-support.
                    """)
	    exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
            handle_abort_optim_workflow ()
            exit_with_errors()

    if os.path.isfile (BRIDGE_SMU_INSTALL_MARKER_FILE):
        os.unlink (BRIDGE_SMU_INSTALL_MARKER_FILE)
    # Fill the data
    SUPPORTED_VMTYPE = ["host", "sysadmin", "xr"]
    pkgs = []
    giso_pkgs = []
    location_vm_dict = {}
    if options.participants and options.participants[0] == ALL_NODES:
        options.participants = []
    inst = inst_config.Inst_Create_Config(CFGFILE, options.participants)
    if not replace:
        new_pkglist_act = sanitize_superseded_list([x.name_on_router for x in pkgstate.to_activate])
        new_pkgstate_act_list = [x for x in pkgstate.to_activate if x.name_on_router in new_pkglist_act]
        pkgstate.to_activate = new_pkgstate_act_list
        pkg_list_act = set([x.name_on_router for x in pkgstate.to_activate])
    else:
        pkg_list_act = sanitize_superseded_list (pkg_act_list)
    logger.info ("Optimized list to prepare after sanitizing input list for "
                        "superseded packages:\n\t%s"%("\n\t".join (pkg_list_act)))
    logger.info ("Package list:")
    upd_message = "\n%s"%('\n'.join (pkg_list_act))
    upd_message += "\ninstall operation %s is in progress"%(str(new_oid))
    update_status_optim_prep (message = upd_message)
    for pkg in pkg_list_act:
        logger.info ('\t%s'%(pkg))

    signal.signal(signal.SIGINT, signal.SIG_IGN)
    logger.info('The operation is past point-of-no-return and can no longer be stopped!')
    logger.info ('Action 1: install prepare action started')
    logger.info ("Triggering prepare operation.\nThis may take a while...")
    img = [i for i in pkg_list_act if ('-mini-' in i or '-golden' in i )][0]
    pkgs = [i for i in pkg_list_act if not ('-mini-' in i or '-golden' in i )]
    try:
        platform = pkgstate.platform
    except:
        platform = img.split('-')[0]
    # If img is giso, check for the GISO rpms packaged per vmtype.
    if '-golden' in img:
        giso_pkg = []
        iso = os.path.join ("/install_repo/gl/calvados", img)
        for vmtype in SUPPORTED_VMTYPE:
            cmd = "/pkg/bin/sdr_actioncmd  \
                  '/opt/cisco/calvados/bin/install-functions.py  \
                  read_giso_rpms_mdata %s %s'"%(iso, vmtype)
            giso_pkg_str = run_cmd (cmd)
            if giso_pkg_str == sdr_actioncmd_error:
                raise RuntimeError("Error: Unable to create prepare enviroment")
            giso_pkg = giso_pkg_str.split()
            if giso_pkg:
                giso_pkgs += giso_pkg
            
    # Check if we are allowed to get to V2 with package list provided.
    # Also remove any XR SMU from package list if base package is not provided.
    if not options.replace:
        pkg2check = pkgs + giso_pkgs
        v2basepkgs = []
        skipped_pkgs = []
        opt_rpms = []
        for pkg in pkg2check:
            if package.is_smu_pkg (pkg):
                continue
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)', pkg) 
            if m:
                (path, name, version, release) = m.groups()
                if name in xractive_pkgs:
                    xractive_pkgs.remove (name)

        # Check if V2 version has this bundle instance defined.
        activepkgs = xractive_pkgs.copy ()
        iso = os.path.join ("/install_repo/gl/calvados", img)
        try:
            opt_rpms= package.get_iso_repo_mdata (iso)[2]
        except:
            opt_rpms = []
        if len(xractive_pkgs):
            for x in xractive_pkgs:
                if x not in opt_rpms:
                    logger.debug ("Info: Package %s not available in %s. Skipping."
                                        %(x, options.to_version))
                    activepkgs.remove (x)

        if len (activepkgs):
            logger.error ("Error! The following package(s) is/are required "
                                    "to be activated as part of this operation:")
            logger.error ("\n\t".join(activepkgs))     
            handle_abort_optim_workflow ()
            exit_with_errors()

        # Get all XR base packages (only optional).
        for pkg in pkg2check:
            if package.is_admin_pkg (platform, pkg) or package.is_smu_pkg(pkg):
                continue
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)', pkg)
            if m:
                v2basepkgs.append (m.groups()[1])
            
        for pkg in pkgs:
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)', pkg) 
            if m:
                (path, name, version, release) = m.groups()
                if not package.is_admin_pkg (platform, pkg):
                    if package.is_smu_pkg (pkg):
                        if name not in opt_rpms:
                            continue
                        if name not in v2basepkgs:
                            skipped_pkgs.append (pkg)
        if len(skipped_pkgs):
            logger.info ("Following packages are skipped from activation "
                         "as they do not have corresponding base package installed.")
            logger.info ("\n\t".join(skipped_pkgs))
            pkgs = filter(lambda i: i not in skipped_pkgs, pkgs)


    inst.MiniImage = img
    inst.PkgList = ','.join(pkgs)
    inst.OPID = new_oid
    inst.force = False
    if options.force:
        inst.force = True

    ''' 
    Collect parallel prepare log when downgrade happened from any version >= 7.x.x 
    to any version < 7.x.x
    '''
    pp_version = 7 
    to_ver = int(options.to_version[0 : 1])
    if (to_ver < pp_version):
        inst.isdowngrade = True 
    if options.skip_upgrade_matrix_checks:
        inst.skip_upgrade_matrix_checks = True
 
    #create Config file
    location_vm_dict = inst.create_system_config()
    with open (VM_INFO_FILE, 'w') as fd:
        fd.write (json.dumps (location_vm_dict))
    
    # Add giso packages to pkglist for parsing XR packages.
    # Create XR xrchkpt
    pkgs += giso_pkgs
    chkpt.xr_prep_chkpt_file = PREPCHKPTFILE
    chkpt.OPID = new_oid
    chkpt.MiniBundleISO = img
    chkpt.PkgCount = len(pkgs)
    chkpt.AllPkgs = ' '.join(pkgs)
    chkpt.XrRpms = []
    for p in pkgs:
        if ('sysadmin' not in p and p.startswith(platform)) or '.xr.' in p:
            if '.x86_64' not in p:
                chkpt.XrRpms.append ('%s.x86_64'%(p))
            else:
                chkpt.XrRpms.append (p)
    chkpt.create_xr_instagent_chkpt()
 
    # Copy to sysadmin /root/config_system.cfg
    xr_vm_info = collect_vms_info()
    lead_rp_vm = xr_vm_info.lead_xr_vm
    rps_vm = xr_vm_info.xr_vm_ip

    cmd1 = 'scp %s://%s %s'%(lead_rp_vm,CFGFILE,CFGFILE) 
    cmd = "/pkg/bin/sdr_actioncmd '%s'"%(cmd1)
    output_str = run_cmd (cmd)
    if output_str == sdr_actioncmd_error:
        raise RuntimeError("Error: Unable to copy config file to sysadmin")

    # Compatibility matrix feature will not be supported for xrv9k and iosxrwbd
    platform = get_platform()
    if not os.path.isfile (SKIP_MATRIX_CHECKS) and not options.force and platform not in MATRIX_UNSUPPORTED_PLATFORMS and not options.skip_upgrade_matrix_checks:
        upgrade_matrix = []
        if platform in PRE_POSTCHECKS_SUPPORTED_PLATFORMS and options.replace and options.commit:
           if is_metric_enabled_in_install_profile("upgrade-matrix"):
              upgrade_matrix = precheck_dict['upgrade-matrix']
              action = upgrade_matrix[METRIC_ACTION]
              copy_matrix_files_to_sysadmin()
              check_if_giso_upgrade_is_allowed(pkg_list_act, action)
        else :
           copy_matrix_files_to_sysadmin()
           # If this is an SU with GISO perform upgrade matrix checks right away
           check_if_giso_upgrade_is_allowed(pkg_list_act)

    vrf_str = "/opt/cisco/calvados/bin/vrfch.sh CTRL_VRF"
    cmd1 = '%s python /opt/cisco/calvados/bin/orchestrator.py system %d'%(vrf_str, int(new_oid))
    cmd = "/pkg/bin/sdr_actioncmd '%s'"%(cmd1)
    upd_message = "\nTriggered prepare operation in sysadmin."
    update_status_optim_prep (message = upd_message)
    output_str = run_cmd (cmd)
    if os.path.isfile (USER_ABORT_FILE_PREP):
        logger.error ("Raising abort on prepare as requested by user.")
        raise Exception ("Raising abort on prepare as requested by user.")
    if output_str == sdr_actioncmd_error:
        raise RuntimeError("Error: Unable to prepare partitions")
    if orch_success not in output_str:
        raise RuntimeError("Error: Unable to prepare partitions")
    upd_message = "\nPrepare operation completed in sysadmin."
    update_status_optim_prep (message = upd_message)
    logger.info ("Action 1: install prepare action completed successfully")

    # Fill the calv_instmgr globals wtba from checkpoint data
    try:
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(1) 
        logger.debug(cmd)
        run_cmd(cmd)
    except:
        logger.debug ("Error encountered when reading the prepare checkpoint into calv_instmgr globals")
        pass
 
def optimised_activate():
    try:
        # For install activate, update opid text file with prepare id.
        # Let sdr_instmgr pick same ID as before for activate operation
        #if options.actioptim:
        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
        oidfile = open(op_id_file, 'w')
        oidfile.write(str(new_oid-1))
        oidfile.close()

        # The op_in_progress globals on Sysadmin are already unset at this point, nevertheless unset them again
        unset_op_lock()
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        logger.info ('Prepare operation completed. Trigger activate.\nThis may take a while...')
        if options.background:
            logger.addHandler(ch)
        logger.info ('Prepare completed. Operation ID %d will be '
                        'taken up for activate operation'%(new_oid))
        if options.background:
            logger.removeHandler(ch)
        if options.force:
            autorecover_marker_file="/var/log/install/instdb/autorecover"
            fd = open(autorecover_marker_file, "w") 
            fd.close()
            logger.info('Autorecover marker file created')

        update_single_id_for_edt()
        update_parentid_file(new_oid)
        if options.actioptim:
            logger.info ('Activate operation ID is: %d for \'install activate\' ID:%d'%(new_oid, new_oid))
        else:
            logger.info ('Activate operation ID is: %d for \'install source\' ID:%d'%(new_oid, new_oid))

        cmd = "sdr_instcmd install activate optim noprompt"
        if options.actioptim and options.sync:
            cmd += " synchronous"
        elif not options.actioptim and not options.background:
            cmd += " synchronous"
        upd_message = "\nTriggered activate operation with operation ID %s."%(str(new_oid))
        update_status_optim_prep (message = upd_message)
        subprocess.call (cmd, shell=True)
    except:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug(TB)
        if os.path.exists("/var/log/install/instdb/autorecover"):
            os.remove("/var/log/install/instdb/autorecover")
        logger.error("""
            Error: An exception is hit while waiting for activation of packages
            to complete. Execute "show install request" to check for completion 
            of install activate.
            If you hit same error on retries, please collect "show tech install"
            and contact cisco-support.
            """)
        exit_with_errors()

def sanitize_superseded_list (pkglist, rpms=False):
    pkglist = sorted(pkglist)
    logger.debug ("Input list to be sanitized for supersede logic:\n%s"%(' '.join(pkglist)))
    sanitized_list = []
    if rpms:
        pkglist_x86 = []
        pkglist_arm = []
        for x in pkglist:
            if '.x86_64' in x:
                pkglist_x86.append (x)
            elif '.arm' in x:
                pkglist_arm.append (x)
            else:
                sanitized_list.append (x)
        if pkglist_x86:
            cmd = "/pkg/bin/install-functions.py get_pkgs2consider_list '%s'"%(' '.join(pkglist_x86))
            sanitized_list += run_cmd (cmd).split(',')
        if pkglist_arm:
            cmd = "/pkg/bin/install-functions.py get_pkgs2consider_list '%s'"%(' '.join(pkglist_arm))
            sanitized_list += run_cmd (cmd).split(',')
    else:
        cmd = "/pkg/bin/install-functions.py get_pkgs2consider_list '%s'"%(' '.join(pkglist))
        sanitized_list += run_cmd (cmd).split(',')
    logger.debug ("Sanitized list:\n%s"%(' '.join(sanitized_list)))
    return list(set(sanitized_list))

def check_binary_exists_initrd (input_iso):
    bin2execute_debug = False
    cmd = (
            "isoinfo -R -i '{input_iso}' -x '{file2check}' | "
            "grep '{line2check}'"
                .format (
                    input_iso = input_iso,
                    file2check = "/giso_info.txt",
                    line2check = "BIN_PATH"
                )
          )
    logger.debug ("Command %s"%(cmd))
    binpath = None
    try:
        binpath = run_cmd (cmd)
    except Exception as inst:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug (TB)
        logger.debug ("Binary path not available in GISO. Continue without extracting.")
        pass
    if bin2execute_debug:
        logger.debug ("Debug enabled. Set binpath to cpio and continue for testing.")
        binpath = "bin/cpio"
    return binpath

def check_n_extract_bin2execute (input_iso, stagingdir="/misc/disk1/"):
    bin2execute = None
    stgloc = None
    cwd = os.getcwd()
    if os.path.isfile (input_iso):
        bin2execute = check_binary_exists_initrd (input_iso)

        if not bin2execute:
            return
        bindir = os.path.dirname (bin2execute)
        binfile = os.path.basename (bin2execute)
        cmd = (
                "isoinfo -R -i '{input_iso}' -x '{initrd}' | "
                "zcat -f | "
                "cpio -i --quiet --make-directories '{filepath}'"
                    .format (
                        input_iso=input_iso,
                        initrd="/boot/initrd.img",
                        filepath="%s/*"%(bindir)
                    )
              )
        logger.debug ("Command %s"%(cmd))
        try:
            stgloc = tempfile.mkdtemp (prefix = "%s/"%(stagingdir))
            stgloc_binfile = "%s/%s"%(stgloc, bin2execute)
            os.chdir (stgloc)
            run_cmd (cmd)
            if os.path.isfile (stgloc_binfile):
                try:
                    run_cmd (stgloc_binfile)
                except Exception as inst:
                    exc_info = sys.exc_info()
                    TB = traceback.format_exc()
                    logger.debug (TB)
                    logger.debug ("Unable to execute %s"%(stgloc_binfile))
                    pass
            else:
                logger.debug ("The binary doesnt exist at specified location.")

        except Exception as inst:
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug (TB)
            os.chdir (cwd)
            if os.path.exists (stgloc):
                try:
                    shutil.rmtree (stgloc)
                except:
                    pass
        finally:
            os.chdir (cwd)
            if os.path.exists (stgloc):
                try:
                    shutil.rmtree (stgloc)
                except:
                    pass
    else:
        logger.debug ("ISO %s not available to query."%(input_iso))
    return

def optimised_install_add(pkgstate, pkgdownload):
    ''' Add all packages in parallel '''
    output = ''
    pkgiso = None
    local_repo = pkgstate.tmpstage
    # Sanitize the input add list for only the superseded SMUs while
    # ignoring the superseding SMUs.
    new_pkglist_add = sanitize_superseded_list([x.filename for x in pkgstate.to_add], True)
    new_pkgstate_add_list = [x for x in pkgstate.to_add if x.filename in new_pkglist_add]
    pkgstate.to_add = new_pkgstate_add_list
    packages = " ".join([x.filename for x in pkgstate.to_add])

    if options.replace:
        pkgiso = [x.filename for x in pkgstate.to_add if '.iso' in x.filename][0]
        if pkgiso:
            logger.debug ("Package iso %s"%(pkgiso))
            isoloc = os.path.join (local_repo, pkgiso)
            check_n_extract_bin2execute (isoloc)

    # Get ID for install add.
    add_id = update_operation_id(update_opid=INCR)
    upd_message = "\nAdding packages: %s"%('\n'.join([x.filename for x in pkgstate.to_add]))
    upd_message += "\nAllocated operation ID %d for install add\n"%(add_id)
    update_status_optim_prep (message = upd_message)
    logger.info ("Allocated operation ID %d for install add"%(add_id))
    update_parentid_file(add_id) 
    add_cmd  = "/pkg/bin/sdr_instadd.py -r %s -i %d -p %s -sdr default-sdr"%(local_repo,
            add_id,packages)
    logger.debug(add_cmd)
    cmd = add_cmd.split(" ")
    logger.debug(add_cmd) 
    logger.debug('*'*30)
    try :
        cmd = add_cmd.split(" ")
        add_cmd = [x for x in cmd if x] #Imp else instmgr crashes
        output = run_cmd(' '.join(add_cmd))
        upd_message = "\nNeeded packages added to the system"
        update_status_optim_prep (message = upd_message)
        logger.debug("Add done")

        if os.path.isfile (USER_ABORT_FILE_ADD):
            logger.error ("Raising abort on add as requested by user.")
            raise Exception ("Raising abort on add as requested by user.")
    except :
        global add_oid
        add_oid = add_id
        exc_info = sys.exc_info()
        err_str = str(exc_info[1])
        if "AddError:" in err_str:
            err_str = err_str[err_str.rfind('AddError:')+9:].strip() 
            logger.error("Error in ADD phase: %s "%(err_str))
        elif ("--->" in err_str) and \
            "ERROR" in err_str and \
            err_str.rfind("--->") < err_str.rfind("ERROR"):

            err_str = err_str[err_str.rfind("ERROR")+5:]
            logger.error("Error in ADD phase: %s"%(err_str))
        else:
            TB = traceback.format_exc()
            logger.debug(TB)
            logger.error("""
            Error: An exception is hit while waiting for optimised add to complete.
            If you hit same error on retries, please collect "show tech install"
            and contact cisco-support.
            """)
        handle_add_abort_optim_workflow()
        pkgdownload.clear_staging()
        exit_with_errors()
    finally:
        pkgdownload.clear_staging()

    return [ add_id ]

def add_activate(pkgstate, pkgdownload):

        """ Add activate  given set of packages"""
        declare_install_in_progress_alarm()

        dest_dir = pkgstate.tmpstage
        output = ''
        oper_ids = []
        global add_oid
        #brs_found = False

        ## If there is nothing to add and activate and deactivate
        ## this means the system is already in the same state that
        ## the GISO would bring it to. So, query the user if he
        ## wants to update the label.
        if not pkgdownload.scheme == 'tftp':
            logger.debug("bundle_iso = %s" %(pkgstate.bundle_iso))
            pkgstate.to_add = pkgdownload.not_in_localrepo(pkgstate.to_add)
            pkgstate.to_activate = uniq_package(pkgstate.to_activate)
            pkg_list_activate = "\n\t".join([x.filename for x in pkgstate.to_activate])
            len_to_add = len(pkgstate.to_add)
            len_to_act = len(pkgstate.to_activate)
            len_to_dact = len(pkgstate.to_deactivate)

            if pkgstate.bundle_iso_input and pkgstate.update_giso and options.replace:
                iso_path = os.path.join(pkgstate.tmpstage, 
                                                pkgstate.bundle_iso)
                rpms = pkgstate.bundle_iso_get_sp_file(iso_path,
                        return_rpms=True)
                if rpms != None:
                    for pkg in rpms:
                        if check_if_sp(pkg.filename):
                            active_rpms = [ x.name for x in pkgstate.active_package ]
                            m = re.search(r'(.*)-(\d+.\d+.\d+.*)\.iso',
                                    pkg.filename)
                            sp_name = m.groups()[0]
                            sp_name = sp_name.split("-")
                            sp_name = "-".join(sp_name[:2])
                            if sp_name not in active_rpms:
                                logger.info("Service Pack is there in Golden " 
                                        " iso image which is not active.")
                                pkgstate.to_activate.append(pkg)
                                len_to_act = len(pkgstate.to_activate)

                
            if options.replace and len_to_add == 0 and len_to_act == 0 and len_to_dact == 0 and golden_iso_label_name is not None:
                # dont delete/change below logging. Used for yang log parsing
                logger.info("There is nothing to add and activate as the system is in the same state as what is available in the GISO. However the labels are different.")
                user_str = "Do you want to update label to '%s'?[Yes/No][Yes]: " %(golden_iso_label_name)
                logger.debug("Updating label to '%s'" %(golden_iso_label_name))
                logger.debug("User_str = %s" %(user_str))
                if not pkgdownload.options.noprompt:
                    response = ''
                    signal.alarm(TIMEOUT)
                    while True:
                        response = raw_input(user_str)
                        if response.lower() == "no" :
                            logger.info("Install operation %s aborted\n" %(new_oid))
                            if options.replace and options.commit:
                                logger.info("Install operation %s completed with errors\n" %(new_oid))
                            update_telemetry_event()
                            return -1
                        elif response.lower() == "yes" or len(response) == 0:
                            break
                        else:
                            logger.info("Invalid: %s. Enter 'Yes' or 'No'" %(response))
                            continue
                    signal.alarm(0)
                    
                activate_cmd = "source /pkg/bin/install-functions.sh; update_label %s" %(golden_iso_label_name)
                try :
                    status = subprocess.call(activate_cmd, shell=True)
                except :
                    exc_info = sys.exc_info()
                    TB = traceback.format_exc()
                    logger.debug(TB)
                    logger.error("Label update failed")
                    logger.error(output)
                    logger.info(output)
                    logger.error("""
                        Error: An exception is hit while waiting for activation of packages
                        to complete. Execute "show install request" to check for completion 
                        of install activate.
                        If you hit same error on retries, please collect "show tech install"
                        and contact cisco-support.
                        """)
                    logger.info("Install operation %s aborted\n" %(new_oid))
                    if options.replace and options.commit: 
                        logger.info("Install operation %s completed with errors\n" %(new_oid))
                    update_telemetry_event()
                    clear_install_in_progress_alarm() 
                    return -1

                if status < 0 :
                    logger.error("Label update operation failed")
                    logger.info("Install operation %s aborted\n" %(new_oid))
                    if options.replace and options.commit:
                        logger.info("Install operation %s completed with errors\n" %(new_oid))
                    update_telemetry_event()   
                    clear_install_in_progress_alarm()
                    return -1

                logger.info("Install operation %s finished successfully\n" %(new_oid))
                if options.replace and options.commit:
                    logger.info("Install operation %s completed without error\n" %(new_oid))
                update_telemetry_event()    

                '''telemetry event when label gets change '''
                cmd = "/pkg/bin/sdr_actioncmd \'XR_YANG_TELE_LABEL_CHANGE:%s\'" %new_oid
                status, cmd = commands.getstatusoutput(cmd)
                if status:
                    logger.debug("ERROR: command execution is unsuccessfull")
                else:
                    logger.debug(cmd)
 
                clear_install_in_progress_alarm()
                return 0

        # Add workflow for non tftp.
        if not pkgdownload.scheme == 'tftp':
            if pkgstate.skipped_as_active :
                # dont change/delete below msg. Used for yang log parsing
                msg = "\nSkipped downloading active packages:"
                skipped_active  = list(set(pkgstate.skipped_as_active))
                logger.info("%s\n\t%s"%(msg,"\n\t".join(skipped_active)))
    
            if pkgstate.skipped_as_inactive :
                # dont change/delete below msg. Used for yang log parsing
                msg = "\nSkipped downloading inactive packages:"
                skipped_inactive  = list(set(pkgstate.skipped_as_inactive))
                logger.info("%s\n\t%s"%(msg,"\n\t".join(skipped_inactive)))
                if options.replace and options.commit and not options.force \
                        and is_metric_enabled_in_install_profile("disk-space"):
                   do_disk_space_prechecks(skipped_inactive[0])
    
            #if brs_found:
            #    msg = "Bridge SMU found in GISO as follow:"
            #    logger.info("%s\n\t%s"%(msg,"\n\t".join(pkgstate.brs_rpms_in_bundle)))
            #package download is complete, now trigger install add
            # Add package which are not in local repo
            pkgstate.to_add = pkgdownload.not_in_localrepo(pkgstate.to_add)
            #pkgstate.to_add = pkgdownload.not_in_localrepo(pkgstate.brs_rpms_in_bundle)


            ## If it is 'replace' feature, ensure that GISO is added(if not in repo)
            if pkgdownload.options.replace:
                temp_add = package.Package()
                temp_bundle_iso = pkgstate.bundle_iso
                temp_bundle_iso = temp_bundle_iso.replace('.iso','')
                if is_new_label_format is False:
                    if golden_iso_label_name:
                       temp_bundle_iso = temp_bundle_iso[:-len(golden_iso_label_name)-1]
                       temp_bundle_iso = temp_bundle_iso + '-' + golden_iso_label_name
                logger.debug("iso-name = %s" %(temp_bundle_iso))
                if not pkgstate.chk_giso_in_inactive(temp_bundle_iso):
                    temp_add.filename = pkgstate.bundle_iso
                    pkgstate.to_add.append(temp_add)
                    logger.debug("Adding %s to add list" %(temp_bundle_iso))
                else:
                    logger.debug("%s in inactive list" %(temp_bundle_iso))
                for filename in pkgstate.rpms_in_bundle:
                    for pkg_obj in pkgstate.to_add :
                        if pkg_obj.filename == filename:
                            pkgstate.to_add.remove(pkg_obj)

            logger.debug('Package list before Uniqe list: %s'%("\n\t".join([x.filename for x in pkgstate.to_add])))
            pkgstate.to_add = uniq_package(pkgstate.to_add)
            pkg_list_add = "\n\t".join([x.filename for x in pkgstate.to_add])
            # There should be something to add else don't issues install add command
            if check_platform_supports_optim_wf(pkgstate.platform) and \
                        options.upgrade and options.to_version:
                update_status_optim_prep (init = True)
                upd_message = "User %s, Op Id %s"%(os.getenv('AAA_USER'), str(new_oid))
                upd_message += "\ninstall source"
                update_status_optim_prep (message = upd_message)

            if pkg_list_add:
                # dont delete below logging. Used for yang log parsing
                logger.info("Adding packages \n\t%s"%(pkg_list_add))
                pkg_list_add = pkg_list_add.replace("\n\t",' ')
                add_cmd = "sdr_instcmd install add source %s %s" % (dest_dir, pkg_list_add)
                if not os.path.exists('/tmp/no_install_optimization') and \
                        check_platform_supports_optim_wf(pkgstate.platform) and \
                        options.upgrade and options.to_version: 
                    pkg_list_activate = "\n\t".join([x.filename for x in pkgstate.to_activate])
                    try:
                        oper_ids = optimised_install_add(pkgstate, pkgdownload)
                    except:
                        handle_abort_optim_workflow ()
                else:
                    unset_op_lock()
                    oper_ids = add(add_cmd, pkgdownload.options.sync, pkgdownload)
                if oper_ids:
                    ''' 
                    if add operation goes through and giso is there in input
                    create sp file and symlinks for sp, and remove the package
                    from list of activate to be replaced with their sp_name
                    '''
                    if pkgstate.bundle_iso_input and pkgstate.update_giso :
                        iso_path = os.path.join(pkgstate.tmpstage, 
                                                        pkgstate.bundle_iso)
                        rpms = pkgstate.bundle_iso_get_sp_file(iso_path)
                        if rpms:
                            for pkg in rpms:
                                if check_if_sp(pkg.filename):
                                    pkgstate.to_activate.append(pkg)
                    '''
                    Update add_oid with new_oid to ensure next install operation
                    orders accordingly.
                    '''
                    try:
                        oper_ids.sort()
                    except:
                        pass
                    add_oid = oper_ids[-1]
            else:
                # dont delete/change below logging. Used for yang log parsing
                logger.info("There is nothing to add, skipping install add operation")
                '''
                Update add_oid with new_oid to ensure next install operation
                orders accordingly.
                '''
                add_oid = new_oid
            if os.path.exists('/tmp/no_install_optimization'):
                unset_op_lock()
        else : # Add and activate workflow for tftp
            #TFTP scheme , add is passthrough 
            userpkgs = list(set(pkgdownload.options.userpkgs))
            # dont delete below logging. Used for yang log parsing
            logger.info("Adding packages \n\t %s"%(' '.join(userpkgs)))
            # Optim add/activate workflow for tftp
            if not os.path.exists('/tmp/no_install_optimization') and \
                    check_platform_supports_optim_wf(pkgstate.platform) and \
                    options.upgrade and options.to_version:

                if pkgstate.skipped_as_active :
                    # dont change/delete below msg. Used for yang log parsing
                    msg = "\nSkipped downloading active packages:"
                    skipped_active  = list(set(pkgstate.skipped_as_active))
                    logger.info("%s\n\t%s"%(msg,"\n\t".join(skipped_active)))
    
                if pkgstate.skipped_as_inactive :
                    # dont change/delete below msg. Used for yang log parsing
                    msg = "\nSkipped downloading inactive packages:"
                    skipped_inactive  = list(set(pkgstate.skipped_as_inactive))
                    logger.info("%s\n\t%s"%(msg,"\n\t".join(skipped_inactive)))

                #package download is complete, now trigger install add 
                # Add package which are not in local repo
                pkgstate.to_add = pkgdownload.not_in_localrepo(pkgstate.to_add)


                ## If it is 'replace' feature, ensure that GISO is added(if not in repo)
                if pkgdownload.options.replace:
                    temp_add = package.Package()
                    temp_bundle_iso = pkgstate.bundle_iso
                    temp_bundle_iso = temp_bundle_iso.replace('.iso','')
                    if is_new_label_format is False:
                        if golden_iso_label_name:
                           temp_bundle_iso = temp_bundle_iso[:-len(golden_iso_label_name)-1]
                           temp_bundle_iso = temp_bundle_iso + '-' + golden_iso_label_name
                    logger.debug("iso-name = %s" %(temp_bundle_iso))
                    if not pkgstate.chk_giso_in_inactive(temp_bundle_iso):
                        temp_add.filename = pkgstate.bundle_iso
                        pkgstate.to_add.append(temp_add)
                        logger.debug("Adding %s to add list" %(temp_bundle_iso))
                    else:
                        logger.debug("%s in inactive list" %(temp_bundle_iso))
                    for filename in pkgstate.rpms_in_bundle:
                        for pkg_obj in pkgstate.to_add :
                            if pkg_obj.filename == filename:
                                pkgstate.to_add.remove(pkg_obj)

                logger.debug('Package list before Uniqe list: %s'%("\n\t".join([x.filename for x in pkgstate.to_add])))
                pkgstate.to_add = uniq_package(pkgstate.to_add)
                update_status_optim_prep (init = True)
                upd_message = "User %s, Op Id %s"%(os.getenv('AAA_USER'), str(new_oid))
                upd_message += "\ninstall source"
                update_status_optim_prep (message = upd_message)

                pkgstate.to_activate = uniq_package(pkgstate.to_activate)
                pkg_list_activate = "\n\t".join([x.filename for x in pkgstate.to_activate])
                if os.path.exists('/tmp/no_install_optimization'):
                   unset_op_lock()
                try:
                    oper_ids = optimised_install_add(pkgstate, pkgdownload)
                except:
                    handle_abort_optim_workflow ()

                if oper_ids :
                    '''  
                    Update add_oid with new_oid to ensure next install operation
                    orders accordingly.
                    '''
                    try: 
                        oper_ids.sort()
                    except:
                        pass 
                    add_oid = oper_ids[-1]
                
                pkgdownload.clear_staging()
                pkgstate.to_activate = uniq_package(pkgstate.to_activate)
                pkg_list_act = [x.name_on_router for x in pkgstate.to_activate]
                pkg_list_act = " ".join(list(set(pkg_list_act)))
                
                ## If replace is used, then the activate list has to contain
                ## *all* the packages input by the user.
                ## If old-labelling format is used, then along with stripping
                ## the .iso from GISO-name, one should also strip the label(and dot)
                if pkgdownload.options.replace:
                    logger.debug("INFO: replace option used")
                    acti_list = [] 
                    for user_pkg in options.userpkgs:
                        logger.debug("processing %s" %(user_pkg))
                        if ".iso" in user_pkg:
                            user_pkg = user_pkg.replace('.iso','')
                            if is_new_label_format is False:
                              if golden_iso_label_name:
                                ##In old-fmt label was prepended with '.'. Rplc it with '-'
                                ## ncs5500-golden-x-6.5.2.20I.label to
                                ## ncs5500-golden-x-6.5.2.20I-label
                                dot_pos = user_pkg.rfind(".")
                                user_pkg = user_pkg[:dot_pos] + "-" + user_pkg[dot_pos+1:]
                        elif ".rpm" in user_pkg:
                            user_pkg = user_pkg.replace('.rpm','')
                        elif ".pkg" in user_pkg:
                            user_pkg = user_pkg.replace('.pkg','')
                        acti_list.append(user_pkg)
                    pkg_list_act = " ".join(acti_list)
                    logger.debug("INFO: pkg_list_act = %s" %(pkg_list_act))
                if pkg_list_act:
                    logger.info("Activating %s"%(pkg_list_act))

                    if pkgdownload.options.reload:
                        activate_cmd = "sdr_instcmd install activate pkg 0x10000000 %s" % (pkg_list_act)
                    else:
                        activate_cmd = "sdr_instcmd install activate pkg 0x0 %s" % (pkg_list_act)
                    if not pkgdownload.options.restrict_release :
                        activate_cmd = activate_cmd + ' restrict-release'

                    if pkgdownload.options.force:
                        activate_cmd = activate_cmd + ' force'

                    if pkgdownload.options.replace:
                        activate_cmd = activate_cmd + ' replace'

                    if pkgdownload.options.noprompt:
                        activate_cmd = activate_cmd + ' noprompt'

                    if pkgdownload.options.sync:
                        activate_cmd = activate_cmd + ' synchronous'
                    output = '' 
                    cmd = activate_cmd.split(' ')
                    activate_cmd = [x for x in cmd if x] #Imp else instmgr crashes

                    #Create auto-commit marker file
                    if pkgdownload.options.replace and pkgdownload.options.commit:
                        commit_marker_file="/var/log/install/instdb/replace_commit_marker_file.txt"
                        fd = open(commit_marker_file, "w") 
                        if golden_iso_label_name:
                           version_label = options.to_version + " " + golden_iso_label_name 
                        else:
                           version_label = options.to_version

                        fd.write("%s"%(version_label))
                        fd.close()

                    if not os.path.exists('/tmp/no_install_optimization') and \
                           ( options.upgrade or pkgstate.upgrade_giso ) and \
                           check_if_allow_optimized_workflow (pkgstate, pkgdownload.options.replace, pkg_list_act.split()):
                        try:
                            create_activate_env(pkgstate, pkgdownload.options.replace, 
                                        pkg_list_act.split(), xractive_pkgs)
                            if os.path.isfile (UPDATE_STATUS_FILE_CALV):
                                with open(UPDATE_STATUS_FILE_CALV, 'r') as fIn: 
                                    mdata_dict = json.load(fIn)
                                    if mdata_dict.has_key ("ErrGeneric"):
                                        logger.error('Following error(s) occurred in sysadmin vm(s) '
                                                 'during install operation:')
                                        if mdata_dict.has_key ("ErrGeneric"):
                                            flagIPs = mdata_dict["ErrGeneric"].split(',')
                                            if all(re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i) for i in flagIPs):
                                                for ip in flagIPs:
                                                    card = get_card_from_cal_ip(ip)
                                                    logger.error ("Preparation failed on %s"%(card))
                                            else:
                                                logger.error ("Preparation failed with the error:\n%s"%(mdata_dict["ErrGeneric"]))

                                        if options.force:
                                            logger.info ("Ignoring prepare errors on non-RP nodes")
                            optimised_activate()
                            update_status_optim_prep (init = True)
                        except ExitWorkflow:
                            logger.info ("Triggered successful exit of install workflow.")
                            pass
                        except:
                            if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                                os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")

                            bErrFound = False
         
                            if os.path.isfile (UPDATE_STATUS_FILE_CALV):
                                with open(UPDATE_STATUS_FILE_CALV, 'r') as fIn: 
                                    mdata_dict = json.load(fIn)
                                    if 'Errors' in mdata_dict and len(mdata_dict['Errors']) > 0 or \
                                                    mdata_dict.has_key ("ErrGeneric"):
                                        logger.error('Following error(s) occurred in sysadmin vm(s) '
                                                     'during install operation:')
                                        if mdata_dict.has_key ("ErrGeneric"):
                                            flagIPs = mdata_dict["ErrGeneric"].split(',')
                                            if all(re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i) for i in flagIPs):
                                                for ip in flagIPs:
                                                    card = get_card_from_cal_ip(ip)
                                                    logger.error ("Preparation failed on %s"%(card))
                                            else: 
                                                logger.error ("Preparation failed with the error:\n%s"%(mdata_dict["ErrGeneric"]))
                                        bErrFound = True 
                                        ErrNo   = 0  
                                        ErrList = [] 
                                        ErrMsg  = "" 
                                        if mdata_dict.has_key ('Errors'):
                                            for node_ip in mdata_dict['Errors']:
                                                for line in mdata_dict['Errors'][node_ip]:
                                                    ErrNo = ErrNo + 1
                                                    new_err='#%d %s'%(ErrNo, line)
                                                    ErrList.append(new_err)
                                                card = get_card_from_cal_ip(node_ip)
                                                err_msg = '\n\t\t'.join(ErrList)
                                                ErrList = [] # reset the list
                                                ErrNo = 0 #reset it for next node.
                                                ErrMsg = ErrMsg + (""" 
                    Error stack for %s : 
                       %s
                                                                """%(card, err_msg))
                                        logger.error("""
                        %s
                    Please collect 'show tech-support install one-showtech' from XR and 
                    'show tech-support ctrace' from Admin and pass this information to 
                    your TAC representative for support. 
                                                     """%(ErrMsg))
                            if not bErrFound:     
                                logger.error("""
                                Error: An exception is hit while waiting for optimised prepare to complete.
                                If you hit the same error on retries, please collect 'show tech-support install one-showtech'
                                from XR and 'show tech-support ctrace' from Admin and pass this information
                                to your TAC representative for support.
                                """)
                            
                            handle_abort_optim_workflow ()
                            exc_info = sys.exc_info()
                            TB = traceback.format_exc()
                            logger.debug(TB)
                            logger.error("""
                                Error: An exception is hit while waiting for optimised prepare to complete.
                                If you hit same error on retries, please collect "show tech install"
                                and contact cisco-support.
                                """)
                            exit_with_errors()
                    else :
                        try :
                            '''
                            Update operation_id.txt with new_oid to allow prepare 
                            and activate operations with the same id as install source.
                            Once install source is completed, update the file with 
                            add_oid to restore install operation order.
                            '''
                            update_single_id_for_edt()
                            op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                            # Let sdr_instmgr pick same ID as before for activate operation
                            oidfile = open(op_id_file, 'w')
                            oidfile.write(str(new_oid-1))
                            oidfile.close()
                            unset_op_lock()
                            status = subprocess.call(activate_cmd)
                            # Need to find by parsing the activate log here
                            op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                            oidfile = open(op_id_file, 'r+')
                            old_id = oidfile.read()
                            oidfile.close()
                            update_parentid_file(old_id)  
                            logger.info("Activate operation ID is: %s for 'install source' ID:%s"%(old_id,new_oid))
                        except:
                            exc_info = sys.exc_info()
                            TB = traceback.format_exc()
                            logger.debug(TB)
                            logger.error("install activate failed")
                            logger.error(output)
                            logger.error("""
                                Error: An exception is hit while waiting for activation of packages
                                to complete. Execute "show install request" to check for completion
                                of install activate.
                                If you hit same error on retries, please collect "show tech install"
                                and contact cisco-support.
                                """)
                            if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                                os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")
                            return -1
                        if status < 0 :
                            logger.error("install activate operation failed")
                            if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                                os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")
                            return -1


            else:     # No optim workflow for tftp.
                unset_op_lock()
                add_cmd = "sdr_instcmd install add source %s %s"%(pkgdownload.repo, ' '.join(userpkgs))
                logger.debug(add_cmd)
                oper_ids = add(add_cmd, pkgdownload.options.sync, pkgdownload)
                logger.debug('Oper_ids %s'%(" ".join(oper_ids)))
                if oper_ids :
                    '''  
                    Update add_oid with new_oid to ensure next install operation
                    orders accordingly.
                    '''
                    try: 
                        oper_ids.sort()
                    except:
                        pass 
                    add_oid = oper_ids[-1]
                    '''
                    Update operation_id.txt with new_oid to allow prepare 
                    and activate operations with the same id as install source.
                    Once install source is completed, update the file with 
                    add_oid to restore install operation order.
                    '''
                    op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                    # Let sdr_instmgr pick same ID as before for activate operation
                    oidfile = open(op_id_file, 'w')
                    oidfile.write(str(new_oid-1))
                    oidfile.close()
                    if pkgdownload.options.reload:
                        activate_cmd = "sdr_instcmd install activate id 0x10000000 %s " % (" ".join(oper_ids))
                    else:
                        activate_cmd = "sdr_instcmd install activate id 0x0 %s " % (" ".join(oper_ids))

                    if not pkgdownload.options.restrict_release :
                        activate_cmd = activate_cmd + ' restrict-release'
                 
                    if pkgdownload.options.force:
                        activate_cmd = activate_cmd + ' force'

                    if pkgdownload.options.noprompt:
                        activate_cmd = activate_cmd + ' noprompt'

                    if pkgdownload.options.replace:
                        activate_cmd = activate_cmd + ' replace'

                    if pkgdownload.options.sync:
                        activate_cmd = activate_cmd + ' synchronous'

                    output = ''
                    logger.debug(activate_cmd)
                    cmd = activate_cmd.split(' ')
                    activate_cmd = [x for x in cmd if x] #Imp else instmgr crashes

                    #Create auto-commit marker file
                    if pkgdownload.options.replace and pkgdownload.options.commit:
                        commit_marker_file="/var/log/install/instdb/replace_commit_marker_file.txt"
                        fd = open(commit_marker_file, "w") 
                        if golden_iso_label_name:
                           version_label = options.to_version + " " + golden_iso_label_name 
                        else:
                           version_label = options.to_version

                        fd.write("%s"%(version_label))
                        fd.close()

                    try :
                        update_single_id_for_edt() 
                        logger.info("Activating id %s"%(" ".join(oper_ids)))
                        status = subprocess.call(activate_cmd)
                    except:
                        exc_info = sys.exc_info()
                        TB = traceback.format_exc()
                        logger.debug(TB)
                        logger.error("install activate failed")
                        logger.error(output)
                        logger.error("""
                            Error: An exception is hit while waiting for activation of packages
                            to complete. Execute "show install request" to check for completion 
                            of install activate.
                            If you hit same error on retries, please collect "show tech install"
                            and contact cisco-support.
                            """)
                        return -1
                    if status < 0 :
                        logger.error("install activate operation failed")
                        return -1
            #End of tftp add

        # Activate nontftp workflow. Add is done.
        if not pkgdownload.scheme == 'tftp':

            if not oper_ids and pkgstate.bundle_iso_input and pkgstate.update_giso :
                iso_path = os.path.join(pkgstate.tmpstage, 
                                                pkgstate.bundle_iso)
                rpms = pkgstate.bundle_iso_get_sp_file(iso_path, return_rpms = True)
                if rpms:
                    for pkg in rpms:
                        if check_if_sp(pkg.filename):
                            pkgstate.to_activate.append(pkg)

            pkgdownload.clear_staging()
            ''' 
            filter the to_activate package wrt to service pack
            '''
            pkgs = pkgstate.to_activate[:]
            pkgs1 = [] 
            for pkg in pkgstate.to_activate:            
                if check_if_sp(pkg.filename):
                    sp_name = pkg.filename
                    pkgs1 = get_sp_rpms(pkg.filename)
                    break

            for pkg1 in pkgs1:
                for pkg2 in pkgstate.to_activate:
                    if pkg2.filename == pkg1:
                        logger.info("Skipping %s as this is part of "
                                    " Service Pack %s" %(pkg1,sp_name))
                        pkgs.remove(pkg2)
      
            pkgstate.to_activate = pkgs 
            pkgstate.to_activate = uniq_package(pkgstate.to_activate)
            pkg_list_act = [x.name_on_router for x in pkgstate.to_activate]
            pkg_list_act = " ".join(list(set(pkg_list_act)))

            ## If replace is used, then the activate list has to contain
            ## *all* the packages input by the user.
            ## If old-labelling format is used, then along with stripping
            ## the .iso from GISO-name, one should also strip the label(and dot)
            if pkgdownload.options.replace:
                logger.debug("INFO: replace option used")
                acti_list = []
                for user_pkg in options.userpkgs:
                    logger.debug("processing %s" %(user_pkg))
                    if ".iso" in user_pkg:
                        user_pkg = user_pkg.replace('.iso','')
                        if is_new_label_format is False:
                          if golden_iso_label_name:
                            ##In old-fmt label was prepended with '.'. Rplc it with '-'
                            ## ncs5500-golden-x-6.5.2.20I.label to
                            ## ncs5500-golden-x-6.5.2.20I-label
                            dot_pos = user_pkg.rfind(".")
                            user_pkg = user_pkg[:dot_pos] + "-" + user_pkg[dot_pos+1:]
                    elif ".rpm" in user_pkg:
                        user_pkg = user_pkg.replace('.rpm','')
                    elif ".pkg" in user_pkg:
                        user_pkg = user_pkg.replace('.pkg','')
                    acti_list.append(user_pkg)
                pkg_list_act = " ".join(acti_list)
                logger.debug("INFO: pkg_list_act = %s" %(pkg_list_act))

            if pkg_list_act:
                if pkgdownload.options.reload:
                    activate_cmd = "sdr_instcmd install activate pkg 0x10000000 %s" % (pkg_list_act)
                else:
                    activate_cmd = "sdr_instcmd install activate pkg 0x0 %s" % (pkg_list_act)
                if not pkgdownload.options.restrict_release :
                    activate_cmd = activate_cmd + ' restrict-release'
                
                if pkgdownload.options.force:
                    activate_cmd = activate_cmd + ' force'
    
                if pkgdownload.options.replace:
                    activate_cmd = activate_cmd + ' replace'

                if pkgdownload.options.noprompt:
                    activate_cmd = activate_cmd + ' noprompt'

                if pkgdownload.options.sync:
                    activate_cmd = activate_cmd + ' synchronous'

                logger.info("Activating %s"%(pkg_list_act))
                output = ''
                cmd = activate_cmd.split(' ')
                activate_cmd = [x for x in cmd if x] #Imp else instmgr crashes

                #Create auto-commit marker file
                if pkgdownload.options.replace and pkgdownload.options.commit:
                    commit_marker_file="/var/log/install/instdb/replace_commit_marker_file.txt"
                    fd = open(commit_marker_file, "w")
                    if golden_iso_label_name:
                       version_label = options.to_version + " " + golden_iso_label_name 
                    else:
                       version_label = options.to_version

                    fd.write("%s"%(version_label))
                    fd.close()

                if not os.path.exists('/tmp/no_install_optimization') and \
                    ( options.upgrade or pkgstate.upgrade_giso ) and \
                    check_if_allow_optimized_workflow (pkgstate, pkgdownload.options.replace, pkg_list_act.split()):
                    try:
                        create_activate_env(pkgstate, pkgdownload.options.replace, 
                                        pkg_list_act.split(), xractive_pkgs)
                        if os.path.isfile (UPDATE_STATUS_FILE_CALV):
                            with open(UPDATE_STATUS_FILE_CALV, 'r') as fIn:
                                mdata_dict = json.load(fIn)
                                if mdata_dict.has_key ("ErrGeneric"):
                                    logger.error('Following error(s) occurred in sysadmin vm(s) '
                                                 'during install operation:')
                                    if mdata_dict.has_key ("ErrGeneric"):
                                        flagIPs = mdata_dict["ErrGeneric"].split(',')
                                        if all(re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i) for i in flagIPs):
                                            for ip in flagIPs:
                                                card = get_card_from_cal_ip(ip)
                                                logger.error ("Preparation failed on %s"%(card))
                                        else:
                                            logger.error ("Preparation failed with the error:\n%s"%(mdata_dict["ErrGeneric"]))

                                    if options.force:
                                        logger.info ("Autorecovery is selected. "
                                                "Ignoring errors on non-RP nodes")
                        optimised_activate()
                        update_status_optim_prep (init = True)
                    except ExitWorkflow:
                        logger.info ("Triggered successful exit of install workflow.")
                        pass
                    except:
                        if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                            os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")

                        bErrFound = False
                        
                        if os.path.isfile (UPDATE_STATUS_FILE_CALV):
                            with open(UPDATE_STATUS_FILE_CALV, 'r') as fIn:
                                mdata_dict = json.load(fIn)
                                if 'Errors' in mdata_dict and len(mdata_dict['Errors']) > 0 or \
                                                mdata_dict.has_key ("ErrGeneric"):
                                    logger.error('Following error(s) occurred in sysadmin vm(s) '
                                                 'during install operation:')
                                    if mdata_dict.has_key ("ErrGeneric"):
                                        flagIPs = mdata_dict["ErrGeneric"].split(',')
                                        if all(re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', i) for i in flagIPs):
                                            for ip in flagIPs:
                                                card = get_card_from_cal_ip(ip)
                                                logger.error ("Preparation failed on %s"%(card))
                                        else: 
                                            logger.error ("Preparation failed with the error:\n%s"%(mdata_dict["ErrGeneric"]))

                                    bErrFound = True
                                    ErrNo   = 0
                                    ErrList = []
                                    ErrMsg  = ""
                                    if mdata_dict.has_key ('Errors'):
                                        for node_ip in mdata_dict['Errors']:
                                            for line in mdata_dict['Errors'][node_ip]:
                                                ErrNo = ErrNo + 1
                                                new_err='#%d %s'%(ErrNo, line)
                                                ErrList.append(new_err)
                                            card = get_card_from_cal_ip(node_ip)
                                            err_msg = '\n\t\t'.join(ErrList)
                                            ErrList = [] # reset the list
                                            ErrNo = 0 #reset it for next node.
                                            ErrMsg = ErrMsg + ("""
                Error stack for %s : 
                   %s
                                                            """%(card, err_msg))
                                    logger.error("""
                    %s
                Please collect 'show tech-support install one-showtech' from XR and 
                'show tech-support ctrace' from Admin and pass this information to 
                your TAC representative for support. 
                                                 """%(ErrMsg))
                        if not bErrFound:        
                            logger.error("""
                            Error: An exception is hit while waiting for optimised prepare to complete.
                            If you hit the same error on retries, please collect 'show tech-support install one-showtech'
                            from XR and 'show tech-support ctrace' from Admin and pass this information
                            to your TAC representative for support.
                            """)

                        handle_abort_optim_workflow ()
                        exc_info = sys.exc_info()
                        TB = traceback.format_exc()
                        logger.debug(TB)

                        exit_with_errors()
                else :
                    try :
                        '''
                        Update operation_id.txt with new_oid to allow prepare and activate
                        operations with the same id as install source.
                        Once install source is completed, update the file with add_oid to restore 
                        install operation order.
                        '''
                        update_single_id_for_edt()
                        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                        # Let sdr_instmgr pick same ID as before for activate operation
                        oidfile = open(op_id_file, 'w')
                        oidfile.write(str(new_oid-1))
                        oidfile.close()
                        unset_op_lock()
                        update_single_id_for_edt()
                        status = subprocess.call(activate_cmd)
                        # Need to find by parsing the activate log here 
                        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
                        oidfile = open(op_id_file, 'r+')
                        old_id = oidfile.read()
                        oidfile.close()
                        update_parentid_file(old_id)
                        logger.info("Activate operation ID is: %s for 'install source' ID:%s"%(old_id,new_oid))
                    except:
                        exc_info = sys.exc_info()
                        TB = traceback.format_exc()
                        logger.debug(TB)
                        logger.error("install activate failed")
                        logger.error(output)
                        logger.error("""
                            Error: An exception is hit while waiting for activation of packages
                            to complete. Execute "show install request" to check for completion 
                            of install activate.
                            If you hit same error on retries, please collect "show tech install"
                            and contact cisco-support.
                            """)
                        if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                            os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")
                        return -1
                    if status < 0 :
                        logger.error("install activate operation failed")
                        if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
                            os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")
                        return -1
            else:
                logger.info("There is nothing to activate, skipping install activate operation")
                update_telemetry_event() 
                unset_op_lock()
        return 0

def upgrade_packages(options, pkgs):
    """ Upgrade the packages to new release version """
    cur_version = get_version()
    if cur_version == options.to_version :

        # Check if the labels are same
        label = get_label()
        if label is not None and label == golden_iso_label_name :
            logger.info("\nThe current system version and label are same as in the GISO that you are trying to update to. Exiting.\n")
            logger.info("Install operation %s finished successfully\n" %(new_oid))
            update_telemetry_event() 
            if options.replace and options.commit:
                logger.info("Install operation %s completed without error\n" %(new_oid))
            return 0
        elif label is None and golden_iso_label_name is None:
            logger.error("\nThe current system version is the same as the version of the GISO being used for upgrade.\n"
                        "This action is not supported for a GISO without label. "
                        "Please rebuild the GISO with a label and retry or use a different version GISO without label.\n")
            exit_with_errors()
            return 0
        # Check if GISO in input 
        for name in GOLDEN_ISO_NAMES:
            for p in pkgs :
                if name in p : 
                    logger.info("Updating contents of golden ISO") 
                    upgrade(options,pkgs,cur_version)
                    return 0
        logger.error("Error : Version specified to upgrade is same as running ")
        exit_with_errors()
    upgrade(options,pkgs,cur_version)
    return 0

def giso_in_input(pkgstate, pkgs):
    """ Check if GISO is in-put package list. 
    """
    no_giso_label = False
    iso_plat = pkgstate.platform
    valid_giso_list = [iso_plat + x for x in GOLDEN_ISO_NAMES]

    for iso_name in pkgs :
        # Golden ISO weekly image with version-label.iso format
        m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+.\d+\w*)-(\w*)\.iso', iso_name)
        if not m:
            # Golden ISO CCO image with version-label.iso format
            m = re.search(r'(.*/)*(.*)-(\d+.\d+.\d+\w*)-(\w*)\.iso', iso_name)
        if not m:
            m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+.\d+\w*).(\w*)', iso_name)
        if not m:
            m = re.search(r'(.*/)*(.*)\.iso-(\d+.\d+.\d+\w*).(\w*)', iso_name)
        if not m:
            #Exmple asr9k-goldenk9-x64-6.5.3-prodkey.iso
            m = re.search (r'(.*/)*(.*)-(.*).iso', iso_name)
            if m:
               no_giso_label = True 
        if not m:
            #Example asr9k-goldenk9-x64.iso-6.5.1
            m = re.search (r'(.*/)*(.*)\.iso-(.*)', iso_name)
            if m:
               no_giso_label = True

        if m:
            if no_giso_label:
               (path, name, version) = m.groups()
            else:
               (path, name, version, giso_label) = m.groups()
            if name in valid_giso_list:
                pkgstate.active_version = get_version()
                if version == pkgstate.active_version :
                    pkgstate.update_giso = True
                else :
                    pkgstate.upgrade_giso = True
                pkgstate.bundle_iso_input = True
                pkgstate.bundle_iso = iso_name
                return True
    return False
 
def get_active_pkgs(options,pkgstate, pkgs):
    '''1. If this GISO is in inactive package list, operator should remove that 
          giso and then upgrade with latest GISO. 
       2. If this GISO is active, delta packages will be dded and activated.
       3. There is no deactivation of packages in this operation, so for 
          deactivation,operator needs to deactivate and remove using install cli
    '''

    input_giso = [ x for x in pkgs if '.iso' in x ][0].replace('.iso','')

    p = pkgstate.platform
    # Add list of active optional packages and SMUs 
    a = [ "%s-%s-%s"%(x.name,x.version,x.release) for x in pkgstate.active_package if (x.name.startswith(p) or pkgstate.is_smu(x.filename)) ]
    pkgstate.active_cisco_pkgs = list(set(a))[:]

def upgrade(options, pkgs, v1):
    """ Upgrade a package or everything to NEW version """

    to_add_pkgs = []
    v2 = options.to_version
    if "." not in v2 :
        logger.error("""
                Error : Expected format of version is x.y.z, it should be same
                as it apears in platform-mini-x.iso_x.y.x 
                or platform-mini-x-x.y.x.iso""")
        exit_with_errors()
    elif v2[0] == 'r' : 
        v2 = v2[1:]

    if pkgs:
        upgrade_specified_pkgs = True
    if not v2:
        logger.error("Error : Release version is required for system upgrade")
        exit_with_errors()
    else :
        dest_dir = os.path.join(TMP_ROOT, TMP_PATH, TMP_DIR, v2)
        version = v2
    with PkgDownload(options, version, dest_dir) as pkgdownload :
        pkgstate = package.PackageInfo(options, pkgdownload.v1, act_cmd_op, inact_cmd_op, platN, True)
        pkgstate.tmpstage = dest_dir
        pkgstate.repo_package = pkgdownload.repopkgs
        pkgstate.is_new_label_format = is_new_label_format
        pkgdownload.pkgstate = pkgstate
        if giso_in_input(pkgstate, pkgs) and  v2 == pkgdownload.v1 :
            # If GISO upgrade and same mini version is active
            get_active_pkgs(options, pkgstate, pkgs)
        elif giso_in_input(pkgstate, pkgs) :
            #If Giso upgrade and it's SU
            # In case of GISO, optional packages will not be picked automatically,
            # a right GISO needs to be created so that it contains same or more 
            # optional packages than what is active
            pass 
        else :
            for p in pkgstate.xractive_package :
                if p.type == 'OPTIONAL' or p.type == 'MANDATORY' :
                    if "-mini" in p.name or "-golden" in p.name :
                        v2mini = p.filename.replace(v1,v2)
                        renamed_mini_file = v2mini.replace('.iso','')
                        mini_ver = renamed_mini_file.split('-')[-1]
                        mini_name = renamed_mini_file.split('-')[:-1]
                        mini_name = '-'.join(mini_name)
                        if v2mini.endswith('.iso') :
                            renamed_mini_file = "%s.iso-%s"%(mini_name,mini_ver)
                        elif '.iso' in v2mini :
                            renamed_mini_file = "%s-%s.iso"%(mini_name,mini_ver)
                        if (renamed_mini_file not in options.userpkgs and \
                            v2mini not in options.userpkgs ):
                            options.userpkgs.append(v2mini)
                            pkgs.append(v2mini)
                    else :
                        if pkgstate.hints.is_pkg_bundle(p.name):
                            pkgs.append(p.name)
            pkgs = list(set(pkgs))
        # Check that any packages in input list has same release as iso.
        rel_to_match = 'r' + ''.join(options.to_version.split('.'))
        for pkg in pkgs:
            if ".rpm" in pkg:
                if package.is_tp_pkg(pkgstate.platform, pkg):
                    continue
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)\.rpm', pkg)
                if m:
                    (path, name, version, release, arch) = m.groups()
                    release = release.split('.')[0]
                    if release != rel_to_match:
                        logger.error("Error: Mixed release upgrade not supported."
                                     "ISO release %s rpm release %s pkg %s"
                                     %(rel_to_match, release, pkg))
                        pkgdownload.clear_staging()
                        exit_with_errors()             
                else:
                    logger.error("Error: Unable to parse %s while validating input"
                                 " Please check if pkg name is modified and "
                                 " not following standard RPM format" %(pkg))
                    pkgdownload.clear_staging()
                    exit_with_errors()
        for p in pkgs :
            if not pkgdownload.is_smu(p) and p.endswith('.rpm'):
                m = re.search(r'(.*/)*(.*)-(.*)-(.*)\.(.*)(\.rpm)', p)
                if m:
                    p_name = m.groups()[1]
                    if p_name in pkgs :
                        # If explicit RPM package given , exclude the name
                        pkgs.remove(p_name)

	#if options.replace and orig_golden_iso_name is not None:
	#    pkgs = [orig_golden_iso_name]
        pkgs_temp = []
        for op in pkgs:
            if op == changed_golden_iso_name :
                pkgs_temp.append(orig_golden_iso_name)
            else:
                pkgs_temp.append(op)
        pkgs = pkgs_temp

        if pkgdownload.scheme == 'tftp'and \
                    (os.path.exists('/tmp/no_install_optimization') or \
                     not check_platform_supports_optim_wf(pkgstate.platform)):

            logger.info("""
            Auto dependency management is not possible with TFTP repository.
            Given set of packages will be downloaded and installed if
            dependencies are met. Only full rpm names should be used with TFTP.
            The mini ISO need not be specified, that's included in upgrade.
            """)
        else :
            if pkgdownload.scheme == 'tftp':
                logger.info("""
                Auto dependency management is not possible with TFTP repository.
                Given set of packages will be downloaded and installed if
                dependencies are met. Only full rpm names should be used with TFTP.
                The mini ISO need not be specified, that's included in upgrade.
                """) 
            pkgstate.input_package = pkgdownload.list(options, pkgs, v2)
            pkgstate.filter(v2)
            while pkgstate.to_download_package:
                pkgstate.to_download_package = pkgstate.sanitize_remove_duplicates(pkgstate.to_download_package)
                pkgstate.to_download_package = \
                                    uniq_package(pkgstate.to_download_package)
                dnld=pkgdownload.not_in_localrepo(pkgstate.to_download_package)
                if dnld : 
                    dnld = pkgdownload.add_tp_base(dnld)
                    pkgdownload.get(dnld)

                ''' 
                if any sp is there in input then filter the list of SMUs
                specified in the CLI, remove the SMUs which are there in SP
                '''
                sp_name = ""
                pkg_downloaded = pkgstate.to_download_package[:]
                pkgstate.rpms_in_bundle = []
                for pkg in pkgstate.to_download_package:
                    if check_if_sp(pkg.filename):
                        sp_name = pkg.filename
                        iso_path = os.path.join(pkgstate.tmpstage,pkg.filename)
                        pkgstate.bundle_iso_get_rpms(iso_path)
                        break
                if pkgstate.rpms_in_bundle:
                    for rpm in pkgstate.rpms_in_bundle:
                        for pkg1 in pkgstate.to_download_package:
                            if pkg1.filename == rpm:
                                logger.info("Skipping %s as this is part of "
                                            " Service Pack %s" %(pkg1.filename,
                                                sp_name))
                                pkg_downloaded.remove(pkg1)

                pkgstate.to_download_package = pkg_downloaded
                if pkgstate.bundle_iso_input and pkgstate.update_giso :
                    # We have golden ISO, mount and get packages, 
                    # Skip already active and activate rest of them
                    iso_path = os.path.join(pkgstate.tmpstage,pkgstate.bundle_iso)
                    sp_found_in_giso = pkgstate.bundle_iso_get_rpms(iso_path)
                    if sp_found_in_giso and sp_name:
                        logger.error("Service Pack in input and as well as"
                                     " within GISO can not be used")
                        pkgdownload.clear_staging()
                        exit_with_errors()
              
                    # Remove GISO , we need to add only delta
                    pkgstate.to_download_package = [ x for x in \
                        pkgstate.to_download_package if not x.filename == \
                        pkgstate.bundle_iso ]
                    length_tdp = len(pkgstate.to_download_package);
                    pkgstate.bundle_iso_pkgs = \
                                        pkgdownload.multiarch(pkgstate.bundle_iso_pkgs)
                    pkgstate.to_download_package.extend(pkgstate.bundle_iso_pkgs)
                elif pkgstate.bundle_iso_input and pkgstate.upgrade_giso :
                    #print "Upgrade is TBD"
                    pass
                pkgstate.downloaded_package.extend(pkgstate.to_download_package)
                # Download was success to remove from list of to_download
                pkgstate.to_download_package = []
                # Check compatibility , there might be need of fetching more
                err_package = []
                if pkgstate.update_giso or pkgstate.upgrade_giso:
                    # If its GISO the package to be activated is all packages in 
                    # GISO minus the active packages, no compatibility check to  
                    # be done
                    pkgstate.to_activate = uniq_package(pkgstate.downloaded_package[:])
                    more_package = []
                else :
                    more_package, err_package = pkgstate.checkcompat_on_su()

                if len (err_package):
                    logger.error("Error: Following packages not found in repository")
                    logger.error(",".join(set(err_package)))
                    pkgdownload.clear_staging()
                    exit_with_errors()

                if len (more_package):
                    logger.debug ("Info: Additional packages to be fetched.")
                    logger.debug (",".join(set(more_package)))

                # Ignore if already downloaded
                npackages = more_package[:]
                for np in npackages:
                    for dp in pkgstate.downloaded_package:
                        if pkgdownload.is_smu (dp.filename):
                            continue
                        if np == dp.name:
                            more_package.remove(np)
        
                if more_package:
                    pkgstate.input_package = pkgdownload.list(options, more_package)
                    pkgstate.filter()
    
        if (not os.path.exists('/tmp/no_install_optimization') and \
                check_platform_supports_optim_wf(pkgstate.platform) and \
                options.upgrade and options.to_version) or \
                not pkgdownload.scheme == 'tftp':
            pkgstate.to_add = list(set(pkgstate.downloaded_package))  

        if pkgstate.unresolvedpkgs:
            logger.error("Error: Following packages not found in repository")
            logger.error(",".join(set(pkgstate.unresolvedpkgs)))
            pkgdownload.clear_staging()
            exit_with_errors()
    
        return add_activate(pkgstate, pkgdownload)

def check_if_running_op():
    cmd = "sdr_instcmd show install request"
    status, output = commands.getstatusoutput(cmd)
    if output:
        if oper_none_re.search(output):
            return False, ""
        else:
            return True, output 
    else:
        return True, ""
        

def get_version():
    """
    Get current base software version running
    """
    version=None
    cmd= "/pkg/bin/ng_show_version | grep  Version | tail -1"
    status, output = commands.getstatusoutput(cmd)
    if not status:
       version=output.split(' ')[-1]
    if not version:
        logger.error("Failed to get SW version : %s" % (output))
        exit_with_errors()

    return version

def get_label():
    """
    Get current label 
    """
    label_re = re.compile(r'.*Label\s+:\s+(?P<label>\S*)')
    cmd = "/pkg/bin/ng_show_version"
    status, output = commands.getstatusoutput(cmd)

    label = label_re.search(output)
    if label:
        label = label.group('label')
        logger.debug("Obtained label: %s\n" %(label))
        if '-' in label:
            temp_label = label.split('-', 1)[1]
            logger.info("Current full-label: %s" %(label))
            logger.info("Current only-label: %s" %(temp_label))
            label = temp_label
            logger.info("Current label: %s" %(label))
        else:
            label = None
            logger.info("Current label: <None>")
    else:
        label = None
        logger.info("Current label: <None>")

    options.curr_label = label
    return label


def get_cal_cmd(command):
  """This is used to generate the equivalent command to run on XR for CAL"""
  cmd_file = "/tmp/ciscoinstalldepcalc.txt"
  cal_tool_name = "/pkg/sbin/admin-cli-proxy-xr_static"
  fd =open(cmd_file,"w")
  tool_name = cal_tool_name + " -n -p XR <<'EOF'"
  fd.write("%s\n%s\nEOF"%(tool_name,command))
  fd.close()
  os.system("chmod +x %s"%(cmd_file))
  return cmd_file

def its_not_lcvm(vm_ip,loc):
    cmd =  "show node-inventory summary location %s | i %s " % (loc,vm_ip)
    cmd = get_cal_cmd(cmd)
    try:
        status,pr = commands.getstatusoutput(cmd)
    except :
        # assume to be in good condition :-)
        return True
    if 'IOS-XR-LC' not in pr:
        return True
    else :
        return False

def get_other_rps_vms():
  ''' Get list of all the RP VMs where the log files needs to be copied  '''
  logger.debug("Getting list of all other RPs XR VM")
  vm = []
  status, cmd_op = commands.getstatusoutput('/pkg/bin/placed_show -p sdr_instmgr')
  if not status:
    for line1 in cmd_op.splitlines():
      if "sdr_instmgr" in line1:
        loc = line1.split()[3].rsplit("/", 1)[0]

  cmd =  "show sdr"
  cmd = get_cal_cmd(cmd)
  find = 0
  try:
    status,pr = commands.getstatusoutput(cmd)
    if not status:
      for line in pr.splitlines():
        if "SDR" in line:
          if find == 1:
            return vm
          else:
            del vm[:]
        if "RUNNING" in line:
          location = line.split()[0].rsplit("/", 1)[0]
          matchObj = re.match(r'(.*)RP(.*)', location, re.M | re.I)
          if not matchObj:
              matchObj = re.match(r'(.*)RSP(.*)', location, re.M | re.I)
          vm_ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', line)
          if matchObj:
            if(loc == location) and its_not_lcvm(vm_ip[0],loc):
              lead_rp_vm = vm_ip[0]
              find = 1
              continue
            vm.append(vm_ip[0])
  except:
    logger.warning('Warning: Could not get VM info. from calvados')
  os.remove(cmd)
  if find == 1:
    return lead_rp_vm,vm
  else:
    return None,[]

def get_card_from_cal_ip(cal_ip):
    card = cal_ip
    vm_dict = {}
    mdata_vms = {}

    try:
        fVM = open(VM_INFO_FILE, 'r')
    except IOError as e:
        #actually this is error but ignoring it after priniting a msg
        logger.info(str(e))
    except:    
        pass # ignoring any exception that occurred in getting card string
    else:
        with fVM:
            mdata_vms = json.load(fVM)
     
        for key, value in mdata_vms.iteritems ():
            if value.has_key ('sysadmin'):
                vm_dict[value['sysadmin'][0]] = key
         
        if vm_dict.has_key (cal_ip):
            card = vm_dict[cal_ip]

    return card

def copy_log_files(lead_rp_vm, rps_vm):
  '''this is used to copy log file and operation_id.txt to other RPs'''
  for vm in rps_vm:
    try:
      cmd = ('scp %s:%s %s:%s' % (lead_rp_vm,LOGFILE1,vm,LOGFILE1))
      status, pr = commands.getstatusoutput(cmd)
      if status:
        logger.debug("RP Sync failed: Unable to copy log file to other RPs.")
    except:
      logger.warning("RP Sync:Unable to PUSH install log file to other RPs.")
      pass

    try:
      op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
      cmd = ('scp %s:%s %s:%s' % (lead_rp_vm,op_id_file,vm,INSTALL_DB_DIR))
      status, pr = commands.getstatusoutput(cmd)
      if status:
        logger.debug("RP Sync failed: Unable to copy OID file to other RPs.")
    except:
        logger.warning("RP Sync: Unable to PUSH Operation id to other RPs.")
        pass

    if os.path.isfile (BRIDGE_SMU_INSTALL_MARKER_FILE):
      try:
        cmd = ('scp %s:%s %s:%s' % (lead_rp_vm,BRIDGE_SMU_INSTALL_MARKER_FILE,
                                  vm,BRIDGE_SMU_INSTALL_MARKER_FILE))
        status, pr = commands.getstatusoutput(cmd)
        if status:
          logger.debug("RP Sync: Unable to copy {} to other RPs.".format
                                        (BRIDGE_SMU_INSTALL_MARKER_FILE))
      except:
        logger.warning ("RP Sync: Unable to push {} to other RPs.".format
                                        (BRIDGE_SMU_INSTALL_MARKER_FILE))
        pass

    if os.path.isfile (PARENT_ID_FILE):
      try:
        cmd = ('scp %s:%s %s:%s' % (lead_rp_vm, PARENT_ID_FILE,
                                  vm, PARENT_ID_FILE))
        status, pr = commands.getstatusoutput(cmd)
        if status:
          logger.debug("RP Sync: Unable to copy {} to other RPs.".format
                                        (PARENT_ID_FILE))
      except:
        logger.warning ("RP Sync: Unable to push {} to other RPs.".format
                                        (PARENT_ID_FILE))
        pass

def copy_install_marker_file(lead_rp_vm, rps_vm):
  '''this is used to copy install_marker_file.txt to other RPs'''
  for vm in rps_vm:
    try: 
      cmd = ('scp %s:%s %s:%s' % (lead_rp_vm,INSTALL_MARKER_FILE,vm,INSTALL_DB_DIR))
      status, pr = commands.getstatusoutput(cmd)
      if status:
        logger.debug("RP Sync failed: Unable to copy install_marker_file.txt to other RPs")
    except:
      logger.warning("RP Sync:Unable to PUSH install_marker_file.txt to other RPs")

def check_admin_connectivity():
  '''this will be used to check the connectivity with admin'''
  cmd =  "show sdr"
  cmd = get_cal_cmd(cmd)
  try:
    pr = run_cmd(cmd)
    if 'can not connect to admin plane' in pr:
      if not log_file_exist():
        update_operation_id(options.in_opid, update_opid=DECR)
      return 0
    else:
      return 1
  except:
    my_pid=os.getpid()
    cmd="pgrep -P %d " %(my_pid)
    logger.debug(cmd)   
    chld_pid=run_cmd(cmd)
    logger.debug("Child : %s" %(chld_pid))
    lines = chld_pid.splitlines()
    try:
    	for line in lines:
            os.kill (int(line), signal.SIGTERM)
            logger.debug("Killed my child : %s" %(line)) 
    except:
        logger.debug("Unable to kill child process")
    logger.error('Unable to connect to admin plane')
  return 0

def handle_add_abort_optim_workflow():
    logger.debug ("Handling abort for install add source failure in optim mode")
    for file in rem_on_abort:
        if os.path.isfile (file):
            logger.debug ("Remove file %s created as part of this operation"%(file))
            os.unlink (file)

def handle_abort_optim_workflow():
    logger.debug ("Handling abort for install source failure in optim mode")
    for file in rem_on_abort:
        if os.path.isfile (file):
            logger.debug ("Remove file %s created as part of this operation"%(file))
            os.unlink (file)
    # Cleanup the WTBA on calv_instmgr and prepare chkpt on sysadmin 
    file2removeadmin = "/install_repo/gl/instdb/instmgr_prep_chkpt"
    cmd = "/pkg/bin/install_exec_sysadmin \"rm -f %s\" %d" %(file2removeadmin,3)
    logger.debug(cmd)
    run_cmd(cmd)
    unset_op_lock()
    clear_install_in_progress_alarm()
    return

def update_telemetry_event(op_id = None):
    logger.debug("Calling sdr_actioncmd for telemetry api call")
    
    if op_id:
        cmd = "/pkg/bin/sdr_actioncmd \'XR_YANG_REQUEST:%s\'" %op_id
    else:
        cmd = "/pkg/bin/sdr_actioncmd \'XR_YANG_REQUEST:-1\'"

    status, cmd = commands.getstatusoutput(cmd)
    if status:
        logger.debug("ERROR: command execution is unsuccessfull")
    else:
        logger.debug(cmd)

def exit_with_errors(clear_alarm=True):
    logger.error("Ending operation %s " % (new_oid))
    if options.background:
        logger.addHandler(ch)
    logger.info("Install operation %s aborted\n" %(new_oid))
    if options.replace and options.commit:
        logger.info("Install operation %s completed with errors\n" %(new_oid))
    if options.background:
        tzname = time.tzname[0]
        pid = os.getpid ()
        tag = "%INSTALL-INSTMGR-3-OPERATION_ABORT"
        msg = "Install operation %d aborted"%(new_oid)
        message = "%s: install[%s]: %s : %s"%(tzname, pid, tag, msg)
        # Add an entry for show logging.
        log_logger = "%s -s err %s"%("/pkg/bin/logger", message)
        run_cmd (log_logger)
    cmd = "/pkg/bin/sdr_actioncmd 'rm -f /tmp/warm_activate.txt'"
    commands.getstatusoutput(cmd)
    if os.path.exists("/var/log/install/instdb/replace_commit_marker_file.txt"):
        os.remove("/var/log/install/instdb/replace_commit_marker_file.txt")

    if os.path.exists("/var/log/install/instdb/autorecover"):
        os.remove("/var/log/install/instdb/autorecover")

    if os.path.exists("/var/log/install/instdb/force_precheck_in_prog"):
        os.remove("/var/log/install/instdb/force_precheck_in_prog")

    if not log_file_exist():
        update_operation_id(options.in_opid, update_opid=DECR)

    update_telemetry_event(new_oid)

    global post_precheck_opid
    global add_oid

    if (int(post_precheck_opid) > int(add_oid)):
        add_oid = post_precheck_opid
    logger.debug("exit_with_errors : %s" %(add_oid))
    logger.debug("exit_with_errors : %s" %(add_oid))
    if os.path.isfile(BRIDGE_SMU_INSTALL_MARKER_FILE):
        logger.debug("Removing : %s" %(BRIDGE_SMU_INSTALL_MARKER_FILE))
        os.remove(BRIDGE_SMU_INSTALL_MARKER_FILE)
    ''' 
    Update operation_id.txt with add_oid (either same as install 
    source or operation id retrieved from install add) to restore operation
    order.
    '''
    op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
    oidfile = open(op_id_file, 'w')
    oidfile.write(str(add_oid))
    oidfile.close()
    try:
        xr_vm_info = collect_vms_info()
        lead_rp_vm = xr_vm_info.lead_xr_vm
        rps_vm = xr_vm_info.xr_vm_ip
        copy_log_files(lead_rp_vm,rps_vm)
    except:
        logger.info("ERROR: Unable to copy log files to RPs")
    finally:
       tmp=subprocess.call([EXEC_TIMEOUT_CTL,"2"])
       if tmp == 0:
           logger.debug("exec-timeout is resumed.")
       else:
           logger.debug("unable to RESUME exec-timeout")
       unset_op_lock()
       if clear_alarm:
           clear_install_in_progress_alarm()
       uninitialize_file_cleanup()
       clear_staging_root()
       os._exit(1)

def parsecli():
    oparser = optparse.OptionParser()
    oparser.add_option(
        "--upgrade",
        action="store_true",
        dest="upgrade",
        default=False,
        metavar=" ",
        help="Upgrade mandatory package (mini mini) and optional packages to new version")

    oparser.add_option(
        "--update",
        action="store_true",
        dest="update",
        default=False,
        metavar=" ",
        help="Upgrade a package (without mini) new version or update to latest SMUs")

    oparser.add_option(
        "--version",
        dest="to_version",
        default=None,
        metavar="",
        help='To which version to upgrade (e.g. 6.0.1)')

    oparser.add_option(
        "--repo",
        type="string",
        dest="repository",
        default=None,
        metavar="URL",
        help="Repository URL e.g. scp://username@10.2.3.4/path/to/repo")

    oparser.add_option(
        "--opid",
        type="string",
        dest="in_opid",
        default=None,
        help = optparse.SUPPRESS_HELP)

    oparser.add_option(
        "--participants",
        type="string",
        dest="participants",
        default=None,
        help = optparse.SUPPRESS_HELP)    

    oparser.add_option(
        "--force",
        action="store_true",
        dest="force",
        default=False,
        help="Skip dependencies and force installation")

    oparser.add_option(
        "--noprompt",
        action="store_true",
        dest="noprompt",
        default=False,
        help="Do not prompt for reload confirmation during activate operation")

    oparser.add_option(
        "--sync",
        action="store_true",
        dest="sync",
        default=False,
        help="Execute install operation in synchronous mode")
    
    oparser.add_option(
        "--reload",
        action="store_true",
        dest="reload",
        default=False,
        help="Reload the system after successful install activate operation") 

    oparser.add_option(
        "--release",
        action="store_true",
        dest="restrict_release",
        default=True,
        help="Restrict installation from same release")

    oparser.add_option(
        "--replace",
        action="store_true",
        dest="replace",
        default=False,
        help="Replace the current active set with the contents of GISO")

    oparser.add_option(
        "--commit",
        action="store_true",
        dest="commit",
        default=False,
        help="Commit the active software automatically <This option is supported with 'replace' keyword only>")

    oparser.add_option(
        "--prepoptim",
        action="store_true",
        dest="prepoptim",
        default=False,
        help="Prepare the set of input packages in optimized parallel prepare mode.")

    oparser.add_option(
        "--background",
        action="store_true",
        dest="background",
        default=False,
        help="Operation is running in background.")

    oparser.add_option(
        "--actioptim",
        action="store_true",
        dest="actioptim",
        default=False,
        help="Prepare the set of input packages in optimized parallel activate mode.")

    oparser.add_option(
        "--nooptim",
        action="store_true",
        dest="nooptim",
        default=False,
        help="Prepare the set of input packages in traditional mode evenn if platform supports optim mode.")

    oparser.add_option(
        "--instopt",
        action="store",
        dest="instopt",
        type=str,
        default=None,
        help="Print install options to operational log file")

    oparser.add_option(
        "--yuser",
        action="store",
        dest="yuser",
        type=str,
        default=None,
        help="Yang user name")

    oparser.add_option(
        "--aux",
        action="store_true",
        dest="aux",
        default=False,
        help="Bypass admin connectivity for yang")

    oparser.add_option(
        "--skip-upgrade-matrix-checks",
        action="store_true",
        dest="skip_upgrade_matrix_checks",
        default=False,
        help="Skip upgrade compatibility matrix check")

    options, args = oparser.parse_args()
    return options, args

def update_operation_id(bypass = False, update_opid = None):
    #get operation id and add the new id 
    oid = 1

    try:
        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
        oidfile = open(op_id_file, 'r+')
        fcntl.flock(oidfile.fileno(), fcntl.LOCK_EX)
        old_id = oidfile.read()
        oidfile.seek(0)
        if not bypass:
            if update_opid == INCR:
                oid = int(old_id) + 1
            elif update_opid == DECR:
                oid = int(old_id) - 1
            oidfile.write(str(oid))
            oidfile.truncate()
        fcntl.flock(oidfile, fcntl.LOCK_UN)
        oidfile.close()
        return oid
    except IOError:
        try:
            op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
            oidfile = open(op_id_file, 'w')
            fcntl.flock(oidfile.fileno(), fcntl.LOCK_EX)
            oid = 1
            oidfile.write(str(oid))
            fcntl.flock(oidfile, fcntl.LOCK_UN)
            oidfile.close()
            return oid
        except Exception as inst:
            print("""
            Error: An exception is hit while executing the install operation.
            If you hit same error on retries, please collect "show tech install"
            and contact cisco-support.
            """)
            unset_op_lock()
            os._exit(1)
        except:
            unset_op_lock()
            os._exit(1)
    except Exception as inst:
        print("""
        Error: An exception is hit while executing the install operation.
        If you hit same error on retries, please collect "show tech install"
        and contact cisco-support.
        """)
        unset_op_lock()
        os._exit(1)
    except:
        unset_op_lock()
        os._exit(1)

def is_exception_char_in_repo(repo):
    excep_char_list = ["?", "#", "[", "["]
    for  echar in excep_char_list:
        if echar in repo:
            return True
    return False

def hide_inline_passwd(url_str, repo):
    import urlparse
    ret_str  = url_str
    passwd = ""

    if repo:
        if is_exception_char_in_repo(repo):
            raise Exception ("Invalid URL!!!")
        parsed = urlparse.urlparse(repo)
        if parsed.path and '@' in parsed.path:
            raise Exception ("Invalid URL!!!")
        if parsed.password:
            for p in parsed.password:
                passwd += "*"
            original_str = parsed.username + ":" + parsed.password
            new_str      = parsed.username  + ":" + passwd
            ret_str = url_str.replace(original_str, new_str)
    return ret_str

if __name__ == '__main__':
    if len(sys.argv) < 3 or '-h' in sys.argv:
        print ("Error : Insufficient number of arguments to program.")
        os._exit(1)
    if '&' in sys.argv :
        sys.argv.remove('&')

    if 'CLITRIGGER' not in sys.argv :
        print ("Error : Not triggered from CLI, exiting.")
        os._exit(1)
    else :
        sys.argv.remove('CLITRIGGER')
    
    if 'actioptim' not in sys.argv and 'prepoptim' not in sys.argv:
        if 'replace' in sys.argv and 'source' not in sys.argv :
            sys.argv.insert(2,'source')
            sys.argv.insert(4,sys.argv[3])

    cli = sys.argv[:]
    sys.argv = ['--upgrade' if x == "upgrade" else x for x in sys.argv]
    sys.argv = ['--update' if x == "update" else x for x in sys.argv]
    sys.argv = ['--repo' if x == "source" else x for x in sys.argv]
    sys.argv = ['--version' if x == "version" else x for x in sys.argv]
    sys.argv = ['--force' if x == "force" else x for x in sys.argv]
    sys.argv = ['--replace' if x == "replace" else x for x in sys.argv]
    sys.argv = ['--noprompt' if x == "noprompt" else x for x in sys.argv]
    sys.argv = ['--sync' if x == "synchronous" else x for x in sys.argv]
    sys.argv = ['--reload' if x == "reload" else x for x in sys.argv]
    sys.argv = ['--restrict-release' if x == "restrict-release" else x for x in sys.argv]
    sys.argv = ['--commit' if x == "commit" else x for x in sys.argv]
    sys.argv = ['--prepoptim' if x == "prepoptim" else x for x in sys.argv]
    sys.argv = ['--actioptim' if x == "actioptim" else x for x in sys.argv]
    sys.argv = ['--background' if x == "background" else x for x in sys.argv]
    sys.argv = ['--nooptim' if x == "nooptim" else x for x in sys.argv]
    sys.argv = ['--aux' if x == "aux" else x for x in sys.argv]
    sys.argv = ['--participants' if x == "participants" else x for x in sys.argv]
    sys.argv = ['--opid' if x == "opid" else x for x in sys.argv]
    sys.argv = ['--skip-upgrade-matrix-checks' if x == "skip-upgrade-matrix-checks" else x for x in sys.argv]

    LOGFILE = '/var/log/install/inst_update.log'

    # create logger
    logger = logging.getLogger('install_logger')
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s  %(message)s',"%Y-%m-%d %H:%M:%S")
    formatter_console = logging.Formatter('%(asctime)s %(message)s',"%Y-%m-%d %H:%M:%S")

    #Console message
    ch = logging.StreamHandler(sys.stdout)
    ch.setLevel(logging.INFO)
    ch.setFormatter (formatter_console)

    if not os.path.exists('/var/log/instdb/log'):
        os.makedirs('/var/log/instdb/log')

    # Logs to logfile
    fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1024*100), backupCount=3)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    
    options, args = parsecli()
    if not options.background:
        logger.addHandler(ch)
    if not options.participants:
        options.participants = []
        if options.in_opid:
            options.participants.insert (0, ALL_NODES)
    else:
        options.participants = options.participants.split (' ')

    pids = commands.getoutput("pgrep -f '/pkg/bin/install\\s'")
    no_pids = len(pids.split())
    ret, oput = check_if_running_op()
    if no_pids > 1 or ret is True :
        update_status_optim_prep (init = True)
        if "Autocommit precheck" in oput:
            logger.error("ERROR: %s"%(oput))
        else:
            logger.error ("ERROR: There is already an install operation in progress on XR")
        sys.exit (0)

    #get operation id and add the new id
    new_oid = update_operation_id(options.in_opid, update_opid=INCR)  
    add_oid = new_oid
    post_precheck_opid = new_oid
    # Take the input OID to continue logging
    # Since we continue with existing OID, do ensure that upon end of the 
    # replace operation, we do have the same OID updated to operation_id.txt.
    if options.in_opid:
        new_oid = int(options.in_opid)
        post_precheck_opid -= 1
        add_oid -= 1
 
    signal.signal(signal.SIGALRM, admin_access_check_timeout)
    signal.signal(signal.SIGALRM, prompt_timeout)
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal.SIG_IGN)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    signal.signal(signal.SIGUSR1, signal_handler_terminate)

    #If the script is executed from aux port,
    #it skips checking the admin connectivity
    if not options.participants:
        if options.aux == False:
            signal.alarm(ADMIN_ACCESS_CHK_TIMEOUT) 
            ret_val = check_admin_connectivity()
            signal.alarm(0)
            if ret_val == 0:
                logger.error("Cannot connect to Admin")
                os._exit(1)

    # Set an initial message for 'show install request'.
    # The status file gets removed as part of unlock operation,
    # or removed as part of the abort workflow.
    try:
        update_status_optim_prep (init = True)
        update_status_optim_prep (message = "Install pre-checks in progress.")
        # get active packages 
        xractive_pkgs = get_active_xr_pkgs()
        active_sp = get_active_sp()

        cmd = "sdr_instcmd show install active"
        status, act_cmd_op = commands.getstatusoutput(cmd)

    except:
        pass
    finally:
        platN = get_platform()

    try:
        cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(4)
        logger.debug(cmd)
        status,output = commands.getstatusoutput(cmd)
        if not status:
           if oper_none_re.search(output):
              logger.debug("No install operation in progress on Sysadmin, proceeding..")
              if os.path.isfile(INSTALL_MARKER_FILE):
                  os.remove(INSTALL_MARKER_FILE)
              open(INSTALL_MARKER_FILE, 'a').close()
              try: 
                 xr_vm_info = collect_vms_info()
                 lead_rp_vm = xr_vm_info.lead_xr_vm
                 rps_vm = xr_vm_info.xr_vm_ip
                 copy_install_marker_file(lead_rp_vm,rps_vm)
              except:
                 logger.info("ERROR: Unable to copy install marker file to RPs")
           elif oper_oip_re.search(output):
              print(output)
              os._exit(1)
           elif oper_nodestatus_re.search(output):
              print(output)
              os._exit(1)
           else:
              #This is either internal error or WTBA found case, print the message as is
              print(output)
              unset_op_lock()
              os._exit(1)
    except:
        logger.info ("An internal error occured while trying to acquire operation lock")
        unset_op_lock()
        os._exit(1)
    finally:
        if not oper_none_re.search(output):
           update_status_optim_prep (init = True) 

    # logger for the install logs 
    LOGFILE1 = INSTALL_DB_DIR + 'log/install_operation_' + str(new_oid) +'.log'
    fh1 = logging.handlers.RotatingFileHandler(LOGFILE1, maxBytes=(1024*100), backupCount=3)
    fh1.setLevel(logging.INFO)
    fh1.setFormatter(formatter)
    logging.getLogger("").addHandler(fh1)

    #if options.prepoptim or options.actioptim:

    logger.debug("+"*80)
    if not (options.repository):
        if not options.prepoptim and not options.actioptim:
            print("Repository is required")
            os._exit(1)

    if not options.instopt:
        options.instopt = "install %s"%(' '.join(cli[1:]))

    if options.replace and options.commit and \
       platN in PRE_POSTCHECKS_SUPPORTED_PLATFORMS:
       load_install_profile_prechecks_configs()

    if options.force or (options.replace and options.commit and is_metric_enabled_in_install_profile("core-cleanup")):
        #Cleanup core and kdump files on sysadmin
        try:
            cmd = "/pkg/bin/install_exec_sysadmin \"ls /install_repo\" %d"%(6)
            logger.debug(cmd)
            run_cmd(cmd)
            logger.debug("Core cleaned on sysadmin")
        except:
            logger.debug ("Error while cleanup core files on sysadmin")

        #Cleanup core and kdump files on XR 
        rm_cmd = "source /pkg/bin/install-functions.sh; rm_core_on_all_xr"
        try:
            status = subprocess.call(rm_cmd, shell=True)
            logger.debug(status)
            logger.debug("Core cleaned on XR")
        except:
            logger.debug ("Error while cleanup core files on XRs");

    if platN not in PRE_POSTCHECKS_SUPPORTED_PLATFORMS or \
        (options.replace and options.commit and\
         is_metric_enabled_in_install_profile("file-cleanup")):
        logger.debug("Triggering file cleanup")
        initialize_file_cleanup()

    try :
        if options.yuser != None:
            user = options.yuser
            replace_str = "--yuser %s" %options.yuser
            options.instopt = options.instopt.replace(replace_str, '')
        else:
            user  = os.getenv('AAA_USER')

        if options.participants:
            logger.info ("Resume Install operation %s" % (new_oid))
        else:
            logger.info ("Install operation %s started by %s:" % (new_oid, user))
        try:
            if options.instopt and options.replace:
                opt = options.instopt.strip("\'")
                opt = opt.split()
                unique_list = []
                [unique_list.append(i) for i in opt if i not in unique_list]
                url_str = hide_inline_passwd(' '.join(unique_list), options.repository)
                if options.participants:
                    logger.debug ("  %s" %url_str)
                else:
                    logger.info("  %s" %url_str)
                inst_replace_op_id = new_oid
                inst_replace_url_str = url_str
            elif options.instopt:
                options.instopt = hide_inline_passwd(options.instopt, options.repository)
                logger.info("  %s"%(options.instopt.strip("\'")))
        except:
            logger.error ("ERROR: Invalid URL in input!!!, Please check URL input.")
            logger.error ("ERROR: Please make sure ('?', '#', '[', ']') chars are not part of URL")
            logger.error ("ERROR: Make sure URL syntax is correct as well.")            
            exit_with_errors()
        # Disabling exec-timeout for install update
        tmp=subprocess.call([EXEC_TIMEOUT_CTL,"1"])
        if tmp == 0:
            logger.debug("exec-timeout is suspended.")
        else:
            logger.debug("Unable to SUSPEND exec-timeout")
        main(options,args)
        logger.debug("Install operation %s finished successfully" % (new_oid))
        logger.debug("Ending operation %s " % (new_oid))
        for file in rem_on_sucess:
            if os.path.isfile (file):
                logger.debug ("Remove temporary file %s created as part of this operation"%(file))
                os.unlink (file)
        tmp=subprocess.call([EXEC_TIMEOUT_CTL,"2"])
        if tmp == 0:
            logger.debug("exec-timeout is resumed.")
        else:
            logger.debug("unable to RESUME exec-timeout")

    except Exception as inst:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug(TB)
        logger.error("""
        Error: An exception is hit while executing the install operation.
        If you hit same error on retries, please collect "show tech install"
        and contact cisco-support.
        """)
        exit_with_errors()
    except:
        exc_info = sys.exc_info()
        TB = traceback.format_exc()
        logger.debug(TB)
        exit_with_errors()
    finally:
        if (int(post_precheck_opid) > int(add_oid)):
            add_oid = post_precheck_opid
        logger.debug("Final op id being written to operation_id.txt : %s" %(add_oid))

        ''' 
        Update operation_id.txt with add_oid (either same as install 
        source or operation id retrieved from install add) to restore operation
        order.
        '''
        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
        oidfile = open(op_id_file, 'w')
        oidfile.write(str(add_oid))
        oidfile.close()
        try:
            xr_vm_info = collect_vms_info()
            lead_rp_vm = xr_vm_info.lead_xr_vm
            rps_vm = xr_vm_info.xr_vm_ip
            copy_log_files(lead_rp_vm,rps_vm)
        except:
            logger.debug("INFO: Unable to copy log files to RPs")
        unset_op_lock()

