blob: 633ab908a46afb3616101b55ae1f54b283b1d4f3 [file] [log] [blame]
#!/usr/bin/env python2
'''
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''
import re
import os
import sys
import logging
from ambari_commons import subprocess32
from optparse import OptionParser
import ConfigParser
USAGE = "Usage: %prog [OPTION]... URL"
DESCRIPTION = "URL should point to full tar.gz location e.g.: https://public-repo-1.hortonworks.com/something/ambari-server.tar.gz"
logger = logging.getLogger("install_ambari_tarball")
PREINST_SCRIPT = "preinst"
PRERM_SCRIPT = "prerm"
POSTINST_SCRIPT = "postinst"
POSTRM_SCRIPT = "postrm"
OS_CHECK = "os_check.py"
OS_PACKAGE_DEPENDENCIES = "dependencies.properties"
OS_FAMILY_DESCRIPTION = "resources/os_family.json"
RPM_DEPENDENCIES_PROPERTY = "rpm.dependency.list"
DEB_DEPENDENCIES_PROPERTY = "deb.dependency.list"
FILES_TO_DOWNLOAD = [PREINST_SCRIPT, PRERM_SCRIPT, POSTINST_SCRIPT, POSTRM_SCRIPT, OS_CHECK, OS_FAMILY_DESCRIPTION, OS_PACKAGE_DEPENDENCIES]
ROOT_FOLDER_ENV_VARIABLE = "RPM_INSTALL_PREFIX"
class Utils:
verbose = False
@staticmethod
def os_call(command, logoutput=None, env={}):
shell = not isinstance(command, list)
print_output = logoutput==True or (logoutput==None and Utils.verbose)
if not print_output:
stdout = subprocess32.PIPE
stderr = subprocess32.STDOUT
else:
stdout = stderr = None
logger.info("Running '{0}'".format(command))
proc = subprocess32.Popen(command, shell=shell, stdout=stdout, stderr=stderr, env=env)
if not print_output:
out = proc.communicate()[0].strip('\n')
else:
proc.wait()
out = None
code = proc.returncode
if code:
err_msg = ("Execution of '%s'\n returned %d. %s") % (command, code, out)
raise OsCallFailure(err_msg)
return out
@staticmethod
def install_package(name):
from os_check import OSCheck
logger.info("Checking for existance of {0} dependency package".format(name))
is_rpm = not OSCheck.is_ubuntu_family()
if is_rpm:
is_installed_cmd = ['rpm', '-q'] + [name]
install_cmd = ['sudo', 'yum', '-y', 'install'] + [name]
else:
is_installed_cmd = ['dpkg', '-s'] + [name]
install_cmd = ['sudo', 'apt-get', '-y', 'install'] + [name]
try:
Utils.os_call(is_installed_cmd, logoutput=False)
logger.info("Package {0} is already installed. Skipping installation.".format(name))
except OsCallFailure:
logger.info("Package {0} is not installed. Installing it...".format(name))
Utils.os_call(install_cmd)
class FakePropertiesHeader(object):
"""
Hacky class to parse properties file without sections.
see http://stackoverflow.com/questions/2819696/module-to-use-when-parsing-properties-file-in-python/2819788#2819788
"""
FAKE_SECTION_NAME = 'section'
def __init__(self, fp):
self.fp = fp
self.sechead = '[{0}]\n'.format(FakePropertiesHeader.FAKE_SECTION_NAME)
def readline(self):
if self.sechead:
try:
return self.sechead
finally:
self.sechead = None
else:
return self.fp.readline()
class OsCallFailure(RuntimeError):
pass
class Installer:
def __init__(self, archive_url, root_folder, verbose, skip_dependencies):
splited_url = archive_url.split('/')
self.archive_name = splited_url[-1]
self.base_url = '/'.join(splited_url[0:-1])
self.root_folder = root_folder
self.verbose = verbose
self.skip_dependencies = skip_dependencies
def download_files(self, files_list):
for name in files_list:
dirname = os.path.dirname(name)
if dirname:
Utils.os_call(["mkdir", "-p", dirname])
url = "{0}/{1}".format(self.base_url, name)
logger.info("Downloading {0}".format(url))
Utils.os_call(["wget", "-O", name, url])
def run(self):
self.download_files([self.archive_name] +FILES_TO_DOWNLOAD) # [self.archive_name] +
self.check_dependencies()
self.run_script(PRERM_SCRIPT, ["remove"]) # in case we are upgrading
self.run_script(POSTRM_SCRIPT, ["remove"]) # in case we are upgrading
self.run_script(PREINST_SCRIPT, ["install"])
self.extract_archive()
self.run_script(POSTINST_SCRIPT, ["configure"])
def check_dependencies(self):
from os_check import OSCheck
os_family = OSCheck.get_os_family()
os_version = OSCheck.get_os_major_version()
is_rpm = not OSCheck.is_ubuntu_family()
property_prefix = RPM_DEPENDENCIES_PROPERTY if is_rpm else DEB_DEPENDENCIES_PROPERTY
cp = ConfigParser.SafeConfigParser()
with open(OS_PACKAGE_DEPENDENCIES) as fp:
cp.readfp(FakePropertiesHeader(fp))
properties = dict(cp.items(FakePropertiesHeader.FAKE_SECTION_NAME))
packages_string = None
postfixes = [os_family+str(ver) for ver in range(int(os_version),0,-1)+['']]+['']
for postfix in postfixes:
property_name = property_prefix + postfix
if property_name in properties:
packages_string = properties[property_name]
break
if packages_string is None:
err_msg = "No os dependencies found. "
if self.skip_dependencies:
logger.warn(err_msg)
else:
raise Exception(err_msg)
packages_string = re.sub('Requires\s*:','',packages_string)
packages_string = re.sub('\\\\n','',packages_string)
packages_string = re.sub('\s','',packages_string)
packages_string = re.sub('[()]','',packages_string)
if self.skip_dependencies:
var = raw_input("Please confirm you have the following packages installed {0} (y/n): ".format(packages_string))
if var.lower() != "y" and var.lower() != "yes":
raise Exception("User canceled the installation.")
return
pacakges = packages_string.split(',')
for package in pacakges:
split_parts = re.split('[><=]', package)
package_name = split_parts[0]
Utils.install_package(package_name)
def run_script(self, script_name, args):
bash_args = []
if self.verbose:
bash_args.append("-x")
Utils.os_call(["bash"] + bash_args + [script_name] + args, env={ROOT_FOLDER_ENV_VARIABLE: self.root_folder}, logoutput=True)
class TargzInstaller(Installer):
def extract_archive(self):
Utils.os_call(['tar','--no-same-owner', '-xvf', self.archive_name, '-C', self.root_folder+os.sep], logoutput=False)
class Runner:
def parse_opts(self):
parser = OptionParser(usage=USAGE, description=DESCRIPTION)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="sets output level to more detailed")
parser.add_option("-r", "--root-folder", dest="root_folder", default="/",
help="root folder to install Ambari to. E.g.: /opt")
parser.add_option("-d", "--dependencies-skip", dest="skip_dependencies", action="store_true",
help="the script won't install the package dependencies. Please make sure to install them manually.")
(self.options, args) = parser.parse_args()
if len(args) != 1:
help = parser.print_help()
sys.exit(1)
self.url = args[0]
@staticmethod
def setup_logger(verbose):
logging_level = logging.DEBUG if verbose else logging.INFO
logger.setLevel(logging_level)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging_level)
stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler)
def run(self):
self.parse_opts()
Runner.setup_logger(self.options.verbose)
Utils.verbose = self.options.verbose
# TODO: check if ends with tar.gz?
targz_installer = TargzInstaller(self.url, self.options.root_folder, self.options.verbose, self.options.skip_dependencies)
targz_installer.run()
if __name__ == '__main__':
Runner().run()