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

import sys
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
import copy
import time
import fcntl 
import signal

print "+" * 80
MAX_CLI_LEN = 1020
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']

# 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")
    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):
        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]:
            self.username, self.hostname = repo[1].split('@')
            if ":" in self.username:
                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 :
                    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 :
                    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()
        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()
                    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)
        if self.repopkgs:
            # 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 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
            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 not self.is_smu(fo.filename) or fo.filename in seen_pkgs:
                        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)

        # 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
                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]
                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:
                                    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 ]
            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 self.is_smu(fo.filename) and fo.filename not in seen_pkgs:
                        # 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)
        # 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 ]
            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
                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 :
                    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 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 :
                    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 :
                    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 :
                    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 :
                    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(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:
            if "Node " in line:
                continue;
            if "Packages: " in line:
                continue;
            if "Boot " in line:
                continue;
            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_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
    iso_in_input = False
    signal.signal(signal.SIGALRM, prompt_timeout)
    signal.signal(signal.SIGINT, signal_handler)

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

    ## 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("\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.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

    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:
                logger.debug("replace option used. GISO found: %s" %(pkg))
                giso_found = True
        if giso_found == False:
            logger.info("ERROR: Golden-ISO has to be used when 'replace' option is used")
            exit_with_errors()

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

    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:
        is_committed = check_if_system_is_committed()
        if is_committed:
            logger.debug("System is in committed state")
        else:
            logger.info("ERROR: The system is not in committed state. Please issue 'install commit' and retry this operation")
            exit_with_errors()

    options.userpkgs = pkglist[:]
    if 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 "INFO: User has opted for force installation." \
              " This means install will try to resolve all the dependencies."\
              " However if there are any unresolved dependencies, they will be ignored.\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.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)
                dnld=pkgdownload.not_in_localrepo(pkgstate.to_download_package)
                if dnld : 
                    dnld = pkgdownload.add_tp_base(dnld)
                    pkgdownload.get(dnld)
                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)
            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)
                logger.error("Install add operation failed.")
                return -1
            if oper_success_re.search(output2):
                logger.info(output2)
                logger.info("Install add operation successful")
                add_ids.append(op_id)
            else:
                logger.error(output2)
                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 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)
            len_to_add = len(pkgstate.to_add)
            len_to_act = len(pkgstate.to_activate)
            len_to_dact = len(pkgstate.to_deactivate)

            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:
                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.upper() == "NO" :
                            return 0
                        elif response.upper() == "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 :
                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 :
                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 pkg_list_add:
                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)
                oper_ids = add(add_cmd, pkgdownload.options.sync)
            else:
                logger.info("There is nothing to add, skipping install add operation")
        else :
            #TFTP scheme , add is passthrough 
            userpkgs = list(set(pkgdownload.options.userpkgs))
            logger.info("Adding packages \n\t %s"%(' '.join(userpkgs)))
            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 :
                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.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':
            pkgdownload.clear_staging()
            pkgstate.to_activate = uniq_package(pkgstate.to_activate)
            pkg_list_act = [x.name_on_router for x in pkgstate.to_activate]
            pkg_list_act = " ".join(list(set(pkg_list_act)))

            ## If replace is used, then the activate list has to contain
            ## *all* the packages input by the user.
            ## If old-labelling format is used, then along with stripping
            ## the .iso from GISO-name, one should also strip the label(and dot)
            if pkgdownload.options.replace:
                logger.debug("INFO: replace option used")
                acti_list = []
                for user_pkg in options.userpkgs:
                    logger.debug("processing %s" %(user_pkg))
                    if ".iso" in user_pkg:
                        user_pkg = user_pkg.replace('.iso','')
                        if is_new_label_format is False:
                            ##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:
                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()

                try :
                    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.
                        """)
                    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':
            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 :
            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)
                dnld=pkgdownload.not_in_localrepo(pkgstate.to_download_package)
                if dnld : 
                    dnld = pkgdownload.add_tp_base(dnld)
                    pkgdownload.get(dnld)
                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)
                    pkgstate.bundle_iso_get_rpms(iso_path)
              
                    # 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 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 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)
          vm_ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', line)
          if matchObj:
            if(loc == location):
              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 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:
      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 exit_with_errors():
    logger.error("Ending operation %s " % (new_oid))
    logger.info("Install operation %s aborted\n" %(new_oid))
    cmd = "/pkg/bin/sdr_actioncmd 'rm -f /tmp/warm_activate.txt'"
    commands.getstatusoutput(cmd)
    lead_rp_vm, rps_vm = get_other_rps_vms()
    copy_log_files(lead_rp_vm,rps_vm)
    tmp=subprocess.call([EXEC_TIMEOUT_CTL,"2"])
    if tmp == 0:
        logger.info("exec-timeout is resumed.")
    else:
        logger.info("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(
        "--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>")

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


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

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

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

    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)

    #get operation id and add the new id 
    new_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)
        new_oid = int(old_id) + 1
        oidfile.write(str(new_oid))
        oidfile.truncate() 
        fcntl.flock(oidfile, fcntl.LOCK_UN)
        oidfile.close()
    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)
            new_oid = 1
            oidfile.write(str(new_oid))
            fcntl.flock(oidfile, fcntl.LOCK_UN)
            oidfile.close()

        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)

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

    logger.debug("+"*80)
    if not (options.repository):
        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.info("exec-timeout is suspended.")
        else:
            logger.info("Unable to SUSPEND exec-timeout")
        main(options,args)
        logger.debug("Install operation %s finished successfully" % (new_oid))
        logger.debug("Ending operation %s " % (new_oid))
        tmp=subprocess.call([EXEC_TIMEOUT_CTL,"2"])
        if tmp == 0:
            logger.info("exec-timeout is resumed.")
        else:
            logger.info("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()
