#!/usr/bin/python
#------------------------------------------------------------------
# install -
#
# Copyright (c) 2015-2019 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.miscutils
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

if 'actioptim' not in sys.argv and 'prepoptim' not in sys.argv:
    print "+" * 80
MAX_CLI_LEN = 1020
TIMEOUT_YESNO = 120
VM_INFO_FILE = "/tmp/vm_info.json"
UPDATE_STATUS_FILE_XR = "/tmp/status_update.txt"
UPDATE_STATUS_FILE_CALV = "/tmp/status_update.json"
new_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 by .*')
oper_success_re = re.compile(r'Install operation \d+ finished successfully')


'''
#===============================================================================
#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
INSTALL_DB_DIR = '/var/log/install/instdb/'
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"
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]

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

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

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

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)

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
            self.list = self.sftplistdir

        elif self.repo.split(":")[0] == 'sftp':
            self.get = self.sftpget
            self.list = self.sftplistdir

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

        elif self.repo.split(":")[0] == 'http':
            self.get = self.httpget
            self.list = self.httplistdir

        elif self.repo.split(":")[0] == 'https':
            self.get = self.httpget
            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:':
            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':
                self.username = raw_input("Enter username for %s:" % (self.hostname))
            if not self.password and self.scheme != 'tftp':
                t = "Enter password for %s@%s:" % (self.username, self.hostname)
                self.password = getpass.getpass(t)
        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):
            os.makedirs(self.dest)
        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)
            try:
                if self.validate_ip(self.hostname):
                    ip_addr = self.hostname
                else:
                    ip_addr = self.nslookup(self.hostname)
                cmd = netns_cmd + "/usr/bin/tftp %s -c get %s %s"%(ip_addr,"/" + src_path,os.path.join(self.dest, df))
                if src_f not in self.redownload :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (src_f))
                status, output = commands.getstatusoutput(cmd)
                if status:
                    logger.error("Failed to download %s\n%s" % (self.srcdir, output))
                    exit_with_errors()
            except Exception as inst:
                logger.error("""
                    Either host %s is not reachable or file %s does not exists
                    %s """ % (self.hostname, df, inst.__doc__))
                exit_with_errors()
            except :
                exc_info = sys.exc_info()
                TB = traceback.format_exc()
                logger.debug(TB)
                logger.error("Failed while downloading from TFTP server")
                exit_with_errors()

    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 dir : %s : %s" % (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)
            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 :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (pkg.filename))
                if is_tpnns:
                    with netns.NetNS(nsname='global-vrf'):
                        self.ftp.retrbinary('RETR ' + filename, self.FtpDownload, FTPBUFFER)
                else :
                    self.ftp.retrbinary('RETR ' + filename, self.FtpDownload, FTPBUFFER)
            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 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)
        cmd = 'ls  -al %s' % (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 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 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 : 
                            fo.name_on_router = '.'.join(fo.name_on_router.split('.')[:-1])
                            fo.name_on_router = fo.name_on_router + '-' + golden_iso_label_name

            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.miscutils.compareEVR((n1, v1, "1"), (n2, v2, "1"))
                    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 re.search(r'-golden',j) and self.pkgstate.upgrade_giso:
                    j = '.'.join(j.split('.')[:-1])
                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:
                            	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:
                logger.error("Error: Following packages not found in repository")
                logger.error(",".join(set(input_pkgs)))
                if options.force:
                    logger.warning("WARNING: Continuing as user"\
                                   " has opted for forced installation")
                else:
                    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):
            os.makedirs(self.dest)

        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)
            try:
                cmd = netns_cmd + "/usr/bin/sshpass -p \'%s\' /usr/bin/sftp %s@%s:/%s %s" % (pwd, usr, host, src, dest)
                if pkg.filename not in self.redownload :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (pkg.filename))
                status, output = commands.getstatusoutput(cmd)
                if status:
                    logger.error("Failed to download %s\n%s" % (src, output))
                    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()
        return 0

    def localget(self, pkgs):
        """Download packages from local directory"""
        srcdir = os.path.join("/", self.srcdir)
        if not os.path.exists(self.dest):
            os.makedirs(self.dest)
        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)
            try:
                cmd =  "cp %s %s" % (src, dest)
                if pkg.filename not in self.redownload :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (pkg.filename))
                status, output = commands.getstatusoutput(cmd)
                if status:
                    logger.error("Failed to download %s\n%s" % (src,output))
                    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()
        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):
            os.makedirs(self.dest)
        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)
            try:
                cmd = netns_cmd + "/usr/bin/sshpass -p \'%s\' /usr/bin/scp %s@%s:/%s %s" % (pwd, usr, host, src, dest)
                if pkg.filename not in self.redownload :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (pkg.filename))
                status, output = commands.getstatusoutput(cmd)
                if status:
                    logger.error("Failed to download %s\n%s" % (src,output))
                    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()
        return 0

    def httpget(self, pkgs):
        if not os.path.exists(self.dest):
            os.makedirs(self.dest)
        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)
            try:
                url = self.TranslateURL(src)
                cmd = netns_cmd + "/usr/bin/curl -o %s %s" % (dest, url)
                if pkg.filename not in self.redownload :
                    # dont delete below logging. Used for yang log parsing
                    logger.info("Fetching .... %s" % (pkg.filename))
                status, output = commands.getstatusoutput(cmd)
                if status:
                    logger.error("Failed to download %s\n%s" % (src,output))
                    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()
        return 0

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]
            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]
            logger.info("Label = %s" %(golden_iso_label_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)
    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:
        (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]      
    active_sp = get_active_sp()
    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_platform_supports_optim_wf (platform):
    allow_optim_workflows_plat = [ "ncs5500", "asr9k" ]
    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 signal_handler(signal, frame):
    logger.info('CTRL+C was pressed by user!')
    sys.exit(0)
    
def main(options, pkglist=[]):
    repo = options.repository if options.repository else None
    iso_in_input = False
    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)

    if check_if_running_op() is True:
        logger.info("ERROR: There is already an install operation in progress")
        exit_with_errors()
    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()

    ## 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()
        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("Install update with commit is supported with replace option only")
            exit_with_errors()
        else:
            logger.info("Install replace commit 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()
            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()
                
        if giso_found == False:
            logger.info("ERROR: Golden-ISO has to be used when 'replace' option is used")
            exit_with_errors()
    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()
        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()

            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()
            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:
            iso_in_input = True
            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()
            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")

            
    ## Check if the system is in committed state
    if options.replace or options.upgrade:
        is_committed = check_if_system_is_committed()
        if is_committed:
            logger.debug("System is in committed state")
        else:
            logger.error("ERROR: The system is not in committed state. Please issue 'install commit' and retry this operation")
            exit_with_errors()
    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] == '/' :
        logger.error("Error: %s is not supported repository protocol" % (repo.split(":")[0]))
        exit_with_errors()
    if options.force:
        print "Warning: 'force' option has been disabled." \
              " Install will perform package compatibility check.\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:
        update_packages(options, pkglist)
    elif options.prepoptim or options.actioptim:
        upd_message = "User %s, Op Id %s"%(getpass.getuser(), 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)
            xractive_pkgs = get_active_xr_pkgs()
            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))
                # 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:
                        logger.error('Following error(s) occurred in sysadmin vm(s) during install operation:')
                        bErrFound = True
                        for node_ip in mdata_dict['Errors']:
                            err_msg = '\n\t\t'.join(mdata_dict['Errors'][node_ip])
                            card = get_card_from_cal_ip(node_ip)
                            logger.error("""
                %s : 
                   %s"""%(card, err_msg))
            if not bErrFound:
                logger.error("""
                            Error: An exception is hit while waiting for optimised operation to complete.
                            If you hit same error on retries, please collect "show tech install"
                            and contact cisco-support.
                            """)
            handle_abort_optim_workflow ()
            exc_info = sys.exc_info()
            TB = traceback.format_exc()
            logger.debug(TB)
 
            exit_with_errors()
    elif options.upgrade:
        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)
        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):
    """ Add the software packages, if number of CLI grows more than 1000 split
    in multiple CLIs and add them """
    add_ids = []
    output = '' 
    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 :
            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)
            exit_with_errors()

        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 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(output)
            logger.error("Install add failed")
            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()

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 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" :
                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)
           
    # Fill the data
    SUPPORTED_VMTYPE = ["host", "sysadmin", "xr"]
    pkgs = []
    giso_pkgs = []
    location_vm_dict = {}
    inst = inst_config.Inst_Create_Config(CFGFILE)
    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
    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))

    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 )]
    # 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.
    if not options.replace:
        pkg2check = pkgs + giso_pkgs
        for pkg in pkg2check:
            m = re.search(r'(.*/)*(.*)-(.*)-(.*)', pkg) 
            if m:
                (path, name, version, release) = m.groups()
                if name in xractive_pkgs:
                    xractive_pkgs.remove (name)

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

    inst.MiniImage = img
    inst.PkgList = ','.join(pkgs)
    inst.OPID = new_oid

    #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)
    try:
        platform = pkgstate.platform
    except:
        platform = img.split('-')[0]
    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")
    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 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:
        op_id_file = INSTALL_DB_DIR + 'operation_id.txt'
        oidfile = open(op_id_file, 'r+')
        old_id = int(oidfile.read())
        oidfile.close()
        logger.info ('Prepare operation completed. Trigger activate.\nThis may take a while...')
        if options.background:
            logger.addHandler(ch)
        logger.info ('Prepare completed. A new operation ID %d will be '
                        'taken up for activate operation'%(old_id + 1))
        if options.background:
            logger.removeHandler(ch)
        logger.info ('Activate operation ID is: %d for \'install source\' ID:%d'%((old_id+1), new_oid))
        cmd = "sdr_instcmd install activate optim noprompt"
        if not options.background:
            cmd += " synchronous"
        upd_message = "\nTriggered activate operation with operation ID %s."%(str(old_id + 1))
        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)
        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 optimised_install_add(pkgstate):
    ''' Add all packages in parallel '''
    output = ''
    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])
    # Get a new ID for install add.
    add_id = get_opid()
    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))
    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") 
    except :
        exc_info = sys.exc_info()
        err_str = str(exc_info[1])
        if ("--->" 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.
            """)
        exit_with_errors()
    return new_oid


def add_activate(pkgstate, pkgdownload):
        """ Add activate  given set of packages"""
        dest_dir = pkgstate.tmpstage
        output = ''
        oper_ids = []

        ## 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 == "No" :
                            return 0
                        elif response == "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))
                    return -1

                if status < 0 :
                    logger.error("Label update operation failed")
                    logger.info("Install operation %s aborted\n" %(new_oid))
                    return -1

                logger.info("Install operation %s finished successfully\n" %(new_oid))
                return 0

        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)))
    
            #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:
                    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"%(getpass.getuser(), 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])
                    oper_ids = optimised_install_add(pkgstate)
                else:
                    oper_ids = add(add_cmd, pkgdownload.options.sync)
                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)
            else:
                # dont delete/change below logging. Used for yang log parsing
                logger.info("There is nothing to add, skipping install add operation")
        else :
            #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)))
            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:
                        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"%(getpass.getuser(), 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])
                oper_ids = optimised_install_add(pkgstate)
                pkg_list_act = [x.name_on_router for x in pkgstate.to_activate]
                pkg_list_act = " ".join(list(set(pkg_list_act)))
                if pkg_list_act:
                    logger.info("Activating %s"%(pkg_list_act))

                    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:
                            xractive_pkgs = get_active_xr_pkgs()
                            create_activate_env(pkgstate, pkgdownload.options.replace, 
                                        pkg_list_act.split(), xractive_pkgs)
                            optimised_activate()
                            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")
                            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:
                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)
                logger.debug('Oper_ids %s'%(" ".join(oper_ids)))
                if oper_ids :
                     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

                     try :
                         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

        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:
                            ##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")
                    version_label = options.to_version + " " + golden_iso_label_name 
                    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:
                        xractive_pkgs = get_active_xr_pkgs()
                        create_activate_env(pkgstate, pkgdownload.options.replace, 
                                        pkg_list_act.split(), xractive_pkgs)
                        optimised_activate()
                        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:
                                    logger.error('Following error(s) occurred in sysadmin vm(s) '
                                                 'during install operation:')
                                    bErrFound = True
                                    for node_ip in mdata_dict['Errors']:
                                        card = get_card_from_cal_ip(node_ip)
                                        err_msg = '\n\t\t'.join(mdata_dict['Errors'][node_ip])
                                        logger.error("""
                %s : 
                   %s"""%(card, err_msg))
                        if not bErrFound:        
                            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.
                            """)

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

                        exit_with_errors()
                else :
                    try :
                        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()
                        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")
        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))
            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. 
    """
    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 m:
            (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)
        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))
                        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))
                    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
                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 = pkgstate.checkcompat()
                # Ignore if already downloaded
                npackages = more_package[:]
                for np in npackages:
                    for dp in pkgstate.downloaded_package:
                        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))  
    
        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 
    else:
        return True
        

def get_version():
    """
    Get current base software version running
    """
    version_re = re.compile(r'.*version=(?P<version>\S+).*[Boot image]')
    cmd = "sdr_instcmd show install active"
    status, output = commands.getstatusoutput(cmd)

    version = version_re.search(output)
    if version:
        version = version.group('version')
    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 + " -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:%slog' % (lead_rp_vm,LOGFILE1,vm,INSTALL_DB_DIR))
      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.")

    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.")

def check_admin_connectivity():
  '''this will be used to check the connectivity with admin'''
  cmd =  "show sdr"
  cmd = get_cal_cmd(cmd)
  try:
    status,pr = commands.getstatusoutput(cmd)
    if not status:
      if 'can not connect to admin plane' in pr:
        return 0
      else:
        return 1
  except:
    logger.error('Unable to connect to admin plane')
  return 0

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)
    
    return

def exit_with_errors():
    logger.error("Ending operation %s " % (new_oid))
    if options.background:
        logger.addHandler(ch)
    logger.info("Install operation %s aborted\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)
    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")
    tmp=subprocess.call([EXEC_TIMEOUT_CTL,"2"])
    if tmp == 0:
        logger.debug("exec-timeout is resumed.")
    else:
        logger.debug("unable to RESUME exec-timeout")
    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(
        "--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=None,
        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.")

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

def get_opid ():
    #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)
        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.
            """)
            os._exit(1)
        except:
            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.
        """)
        os._exit(1)
    except:
        os._exit(1)

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 '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]

    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',"%b %d %H:%M:%S")

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

    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)

    #If the script is executed from aux port,
    #it skips checking the admin connectivity
    if 'aux' in sys.argv :
        sys.argv.remove('aux')
    else:
        ret_val = check_admin_connectivity()
        if ret_val == 0:
            logger.error("Cannot connect to Admin")
            os._exit(1)

    pids = commands.getoutput('pgrep install')
    no_pids = len(pids.split())
    if no_pids > 1 or check_if_running_op() is True :
        sys.exit("ERROR: There is already an install operation in progress")

    #get operation id and add the new id 
    new_oid = get_opid()

    # 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)

    options, args = parsecli()
    if options.prepoptim or options.actioptim:
        ch.setFormatter (formatter_console)

    if not options.background:
        logger.addHandler(ch)

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

    try :
        user  = getpass.getuser()
        logger.error("Install operation %s started by %s:" % (new_oid, user))
        # 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))
        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")
        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()
