Hybris-Upgrade in Web-UI with Python and Selenium

#!/usr/bin/env python

"""
hac-updatesystem.py

On a running hybris server as specified by HYBRIS_BASE_URL
do a login and a "updatesystem" in the hybris administration console.
Currently supported hybris releases:
4.4 (4.3), 4.5, 4.6, 4.7, 4.8, 5.4

Specifying the admin password:
- as first command line argument of this script (not recommended!)
- as environment variable ADMINPASSWORD (password won't be visible by "ps")
  (use HISTCONTROL=ignoreboth and set ADMINPASSWORD with leading space to
  keep it out of the shell history)
- if you don't specify it you will be asked for it at startup

Tested on OS X 10.9 with "ports" installed, Firefox 36, python 2.7.9 and
selenium 2.45.0 (python -c "import selenium;print selenium.__version__").


Frank W. Bergmann
March 2015
"""

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import StaleElementReferenceException

from time import sleep
import sys
import os
import re
import getpass


HYBRIS_BASE_URL = 'http://127.0.0.1:9001'
UPDATE_TIMEOUT = 1800


XPATH_LOGIN_BUTTON_44 = '/html/body/table/tbody/tr/td/table/tbody/tr/td/div/form/table/tbody/tr[4]/td[2]/div/a'
XPATH_WRONG_CREDENTIALS_FIELD_44 = '/html/body/table/tbody/tr/td/table/tbody/tr/td/div/form/table/tbody/tr[4]/td'
XPATH_LINK_INITUPDATE_PAGE_44 = '/html/body/div[1]/div[3]/ul/li[3]/a/span'
XPATH_LINK_UPDATE_PAGE_44 = '/html/body/div[2]/div[2]/div/div[2]/ul/li[2]/div/a'
XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_44 = '/html/body/div[2]/div[2]/div/div/div[3]/form/div[3]/input[5]'
XPATH_LOCALIZE_TYPES_CHECKBOX_44 = '/html/body/div[2]/div[2]/div/div/div[3]/form/div[3]/input[6]'
XPATH_UPDATE_BUTTON_44 = '/html/body/div[2]/div[2]/div/div/div[3]/form/div[1]/input'
XPATH_CONTINUE_LINK_44 = '/html/body/div[2]/div[2]/div/a'

XPATH_LOGIN_BUTTON_45 = '/html/body/form/div/ul/li[4]/button'
XPATH_FOOTER_45 = '/html/body/footer/a'
XPATH_PLATFORM_MENU_45 = '/html/body/div/header/nav[1]/ul/li[1]/a'
XPATH_LINK_UPDATE_PAGE_45 = '/html/body/div/header/nav[2]/ul/li[6]/a'
XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_45 = '/html/body/div/div[2]/div/div[2]/div[3]/input[2]'
XPATH_LOCALIZE_TYPES_CHECKBOX_45 = '/html/body/div/div[2]/div/div[2]/div[3]/input[3]'
XPATH_UPDATE_BUTTON_45 = '/html/body/div/div[2]/div/div[2]/button[1]'

XPATH_LINK_UPDATE_PAGE_46 = '/html/body/div/header/nav[2]/ul/li[7]/a'

XPATH_PLATFORM_MENU_54 = '/html/body/div[1]/header/nav[1]/ul/li[1]/a'
XPATH_LINK_UPDATE_PAGE_54 = '/html/body/div[1]/header/nav[2]/ul/li[7]/a'
XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_54 = '/html/body/div[1]/div[2]/div/div[2]/div[3]/input[2]'
XPATH_LOCALIZE_TYPES_CHECKBOX_54 = '/html/body/div[1]/div[2]/div/div[2]/div[3]/input[3]'
XPATH_UPDATE_BUTTON_54 = '/html/body/div[1]/div[2]/div/div[2]/button[1]'


class HybrisHacUpdateSystem():

    def setUp(self):
        self.hybrisVersion = '0.0'
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(2) # seconds
        self.browser.get(HYBRIS_BASE_URL)
        sleep(1)

    def login(self):
        userfield = self.browser.find_element_by_name('j_username')
        userfield.clear()
        #send_keys('admin' + Keys.RETURN)
        userfield.send_keys('admin')
        pwfield = self.browser.find_element_by_name('j_password')
        pwfield.clear()
        pwfield.send_keys(self.password)
        sleep(2)
        self.getLoginButton()
        self.loginButton.click()
        sleep(4)
        try:
            credentialsError = self.browser.find_element_by_xpath(XPATH_WRONG_CREDENTIALS_FIELD_44)
            if 'Wrong credentials' in credentialsError.text:
                self.exit(5, 'detected "Wrong credentials" string on page, exiting')
        except NoSuchElementException:
            pass

    def getLoginButton(self):
        try:
            self.loginButton = self.browser.find_element_by_xpath(XPATH_LOGIN_BUTTON_45)
            self.hybrisVersion = '4.[56]'
            print 'INFO: detected hybris Version 4.5, 4.6, 4.7, 4.8 or 5.? by login button'
            return
        except NoSuchElementException:
            pass
        try:
            self.loginButton = self.browser.find_element_by_xpath(XPATH_LOGIN_BUTTON_44)
            #self.hybrisVersion = '4.4' # 4.3 has same xpath for button
            print 'INFO: detected hybris Version 4.4 or 4.3 by login button'
            return
        except NoSuchElementException:
            pass
        print 'INFO: unknown hybris version'

    def loadUpdatePage44(self):
        try:
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_INITUPDATE_PAGE_44)
            linkUpdatePage.click()
            sleep(2)
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_UPDATE_PAGE_44)
            linkUpdatePage.click()  # looong running http request, no JS!
            sleep(2)
        except NoSuchElementException:
            self.exit(1, 'could not find XPATH_LINK_UPDATE_PAGE')

    def loadUpdatePage45(self):
        try:
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_PLATFORM_MENU_45)
            linkUpdatePage.click()
            sleep(2)
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_UPDATE_PAGE_45)
            linkUpdatePage.click()
            sleep(2)
        except NoSuchElementException:
            self.exit(1, 'could not find XPATH_LINK_UPDATE_PAGE')

    def loadUpdatePage46(self):
        try:
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_PLATFORM_MENU_45)
            linkUpdatePage.click()
            sleep(2)
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_UPDATE_PAGE_46)
            linkUpdatePage.click()
            sleep(2)
        except NoSuchElementException:
            self.exit(1, 'could not find XPATH_LINK_UPDATE_PAGE')

    def loadUpdatePage54(self):
        try:
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_PLATFORM_MENU_54)
            linkUpdatePage.click()
            sleep(2)
            linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_UPDATE_PAGE_54)
            linkUpdatePage.click()
            sleep(2)
        except NoSuchElementException:
            self.exit(1, 'could not find XPATH_LINK_UPDATE_PAGE')

    def loadUpdatePage(self):
        if '4.[56]' == self.hybrisVersion:
            try:
                footerText = self.browser.find_element_by_xpath(XPATH_FOOTER_45).text
                if ' hybris GmbH, 2011' in footerText:
                    self.hybrisVersion = '4.5'
                    print 'INFO: detected hybris Version 4.5 by footer text'
                if ' hybris AG, 2012' in footerText:
                    self.hybrisVersion = '4.6'
                    print 'INFO: detected hybris Version 4.6, 4.7 or 4.8 by footer text'
                if ' hybris AG, 2013' in footerText:
                    self.hybrisVersion = '5.4'
                    print 'INFO: detected hybris Version 5.? by footer text, assuming 5.4'
            except NoSuchElementException:
                pass
        """
        if '4.[56]' == self.hybrisVersion:
            try:
                linkUpdatePage = self.browser.find_element_by_xpath(XPATH_LINK_UPDATE_PAGE_46)
                if linkUpdatePage.get_attribute('href') == (HYBRIS_BASE_URL + "/platform/update"):
                    self.hybrisVersion = '4.6'
                    print 'INFO: detected hybris Version 4.6, 4.7 or 4.8 by link to update page'
                #sleep(30)
            except NoSuchElementException:
                pass
            if '4.[56]' == self.hybrisVersion:
                self.hybrisVersion = '4.5'
                print 'INFO: detected hybris Version 4.5 by link to update page'
        """
        sleep(2)
        print 'INFO: login done, now opening the update page'
        if self.hybrisVersion == '4.4':
            return self.loadUpdatePage44()
        if self.hybrisVersion == '4.5':
            return self.loadUpdatePage45()
        if self.hybrisVersion == '4.6':
            return self.loadUpdatePage46()
        if self.hybrisVersion == '5.4':
            return self.loadUpdatePage54()
        sleep(5)
        self.exit(2, 'unknown hybris version update page')

    def clickUpdateButton44(self):
        sleep(1)
        try:
            updateButton = self.browser.find_element_by_xpath(XPATH_UPDATE_BUTTON_44)
            #createEssentialDataCheckbox = self.browser.find_element_by_xpath(XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_44)
            #localizeTypesCheckbox = self.browser.find_element_by_xpath(XPATH_LOCALIZE_TYPES_CHECKBOX_44)
        except NoSuchElementException:
            self.exit(3, 'could not find XPATH_UPDATE_BUTTON or checkboxes')
        #createEssentialDataCheckbox.click()
        #sleep(1)
        #localizeTypesCheckbox.click()
        #sleep(1)
        print "INFO: now clicking update button"
        updateButton.click()
        sleep(4)
        try:
            continueLink = self.browser.find_element_by_xpath(XPATH_CONTINUE_LINK_44)
        except NoSuchElementException:
            self.exit(4, 'could not find XPATH_CONTINUE_LINK, did an error occured?')
        print "INFO: update finished"
        continueLink.click()
        sleep(2)

    def clickUpdateButton45(self):
        sleep(1)
        try:
            updateButton = self.browser.find_element_by_xpath(XPATH_UPDATE_BUTTON_45)
            #createEssentialDataCheckbox = self.browser.find_element_by_xpath(XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_45)
            #localizeTypesCheckbox = self.browser.find_element_by_xpath(XPATH_LOCALIZE_TYPES_CHECKBOX_45)
        except NoSuchElementException:
            self.exit(3, 'could not find XPATH_UPDATE_BUTTON or checkboxes, is update already running?')
        #createEssentialDataCheckbox.click()
        #sleep(1)
        #localizeTypesCheckbox.click()
        #sleep(1)
        print "INFO: now clicking update button"
        updateButton.click()
        sleep(1)
        c = 0
        while c < UPDATE_TIMEOUT:
            try:
                elem = self.browser.find_element_by_id('inner')
                if 'FINISHED.' in elem.text:
                    break
            except:
                print "waiting... " + str(c)
            sleep(5)
            c += 5
        print "INFO: update finished"

    def clickUpdateButton46(self):
        sleep(1)
        try:
            updateButton = self.browser.find_element_by_xpath(XPATH_UPDATE_BUTTON_45)
            #createEssentialDataCheckbox = self.browser.find_element_by_xpath(XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_45)
            #localizeTypesCheckbox = self.browser.find_element_by_xpath(XPATH_LOCALIZE_TYPES_CHECKBOX_45)
        except NoSuchElementException:
            self.exit(3, 'could not find XPATH_UPDATE_BUTTON, is update already running?')
        #createEssentialDataCheckbox.click()
        #sleep(1)
        #localizeTypesCheckbox.click()
        #sleep(1)
        print "INFO: now clicking on update button"
        updateButton.click()
        sleep(1)
        c = 0
        while c < UPDATE_TIMEOUT:
            try:
                elem = self.browser.find_element_by_id('inner')
                if 'FINISHED.' in elem.text:
                    break
            except:
                print "waiting... " + str(c)
            sleep(5)
            c += 5
        print "INFO: update finished"

    def clickUpdateButton54(self):
        sleep(1)
        try:
            updateButton = self.browser.find_element_by_xpath(XPATH_UPDATE_BUTTON_54)
            #createEssentialDataCheckbox = self.browser.find_element_by_xpath(XPATH_CREATE_ESSENTIAL_DATA_CHECKBOX_54)
            #localizeTypesCheckbox = self.browser.find_element_by_xpath(XPATH_LOCALIZE_TYPES_CHECKBOX_54)
        except NoSuchElementException:
            self.exit(3, 'could not find XPATH_UPDATE_BUTTON, is update already running?')
        #createEssentialDataCheckbox.click()
        #sleep(1)
        #localizeTypesCheckbox.click()
        #sleep(1)
        print "INFO: now clicking on update button"
        updateButton.click()
        sleep(1)
        c = 0
        while c < UPDATE_TIMEOUT:
            try:
                elem = self.browser.find_element_by_id('inner')
                if 'FINISHED.' in elem.text:
                    break
            except:
                print "waiting... " + str(c)
            sleep(5)
            c += 5
        print "INFO: update finished"

    def clickUpdateButton(self):
        if self.hybrisVersion == '4.4':
            return self.clickUpdateButton44()
        if self.hybrisVersion == '4.5':
            return self.clickUpdateButton45()
        if self.hybrisVersion == '4.6':
            return self.clickUpdateButton46()
        if self.hybrisVersion == '5.4':
            return self.clickUpdateButton54()
        self.exit(2, 'unknown hybris version update page')

    def exit(self, retvalue, message):
        self.browser.quit()
        if retvalue != 0:
            print 'ERROR: ' + message
            sys.exit(retvalue)
        else:
            print 'INFO: ' + message
            sys.exit(retvalue)

    def runUpdate(self):
        self.setUp()
        self.login()
        if self.hybrisVersion == '0.0':
            try:
                footer = self.browser.find_element_by_class_name('copyrightFooter').text
            except NoSuchElementException:
                footer = ''
            footerVersion = re.sub(r'.* - ([45]\..)\..* - Copy.*', r'\1', footer)
            if len(footerVersion) > 2:
                self.hybrisVersion = footerVersion
                print 'INFO: detected hybris Version ' + self.hybrisVersion + ' by footer'
        self.loadUpdatePage()
        self.clickUpdateButton()
        self.exit(0, 'browser quit and exit')


def main(argv):
    adminpw = 'NONE'
    try:
        adminpw = os.environ['ADMINPASSWORD']
    except:
        pass
    if len(argv) > 0:
        adminpw = argv[0]
    else:
        if adminpw == 'NONE':
            adminpw = getpass.getpass('admin Password:')
    sys.stdout = os.fdopen(1, 'w', 0)
    updater = HybrisHacUpdateSystem()
    updater.password = adminpw
    updater.runUpdate()

if __name__ == '__main__':
    main(sys.argv[1:])