X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fqa%2Ftasks%2Fcalamari_setup.py;fp=src%2Fceph%2Fqa%2Ftasks%2Fcalamari_setup.py;h=8ef404f1dc8c2537d1616b6b5c94e162d5b9d47d;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/qa/tasks/calamari_setup.py b/src/ceph/qa/tasks/calamari_setup.py new file mode 100644 index 0000000..8ef404f --- /dev/null +++ b/src/ceph/qa/tasks/calamari_setup.py @@ -0,0 +1,467 @@ +""" +Calamari setup task +""" +import contextlib +import logging +import os +import requests +import shutil +import webbrowser + +from cStringIO import StringIO +from teuthology.orchestra import run +from teuthology import contextutil +from teuthology import misc + +log = logging.getLogger(__name__) + + +DEFAULTS = { + 'version': 'v0.80.9', + 'test_image': None, + 'start_browser': False, + 'email': 'x@y.com', + 'no_epel': True, + 'calamari_user': 'admin', + 'calamari_password': 'admin', +} + + +@contextlib.contextmanager +def task(ctx, config): + """ + Do the setup of a calamari server. + + - calamari_setup: + version: 'v80.1' + test_image: + + Options are (see DEFAULTS above): + + version -- ceph version we are testing against + test_image -- Can be an HTTP URL, in which case fetch from this + http path; can also be local path + start_browser -- If True, start a browser. To be used by runs that will + bring up a browser quickly for human use. Set to False + for overnight suites that are testing for problems in + the installation itself + email -- email address for the user + no_epel -- indicates if we should remove epel files prior to yum + installations. + calamari_user -- user name to log into gui + calamari_password -- calamari user password + """ + local_config = DEFAULTS + local_config.update(config) + config = local_config + cal_svr = None + for remote_, roles in ctx.cluster.remotes.items(): + if 'client.0' in roles: + cal_svr = remote_ + break + if not cal_svr: + raise RuntimeError('client.0 not found in roles') + with contextutil.nested( + lambda: adjust_yum_repos(ctx, cal_svr, config['no_epel']), + lambda: calamari_install(config, cal_svr), + lambda: ceph_install(ctx, cal_svr), + # do it again because ceph-deploy installed epel for centos + lambda: remove_epel(ctx, config['no_epel']), + lambda: calamari_connect(ctx, cal_svr), + lambda: browser(config['start_browser'], cal_svr.hostname), + ): + yield + + +@contextlib.contextmanager +def adjust_yum_repos(ctx, cal_svr, no_epel): + """ + For each remote machine, fix the repos if yum is used. + """ + ice_distro = str(cal_svr.os) + if ice_distro.startswith('rhel') or ice_distro.startswith('centos'): + if no_epel: + for remote in ctx.cluster.remotes: + fix_yum_repos(remote, ice_distro) + try: + yield + finally: + if ice_distro.startswith('rhel') or ice_distro.startswith('centos'): + if no_epel: + for remote in ctx.cluster.remotes: + restore_yum_repos(remote) + + +def restore_yum_repos(remote): + """ + Copy the old saved repo back in. + """ + if remote.run(args=['sudo', 'rm', '-rf', '/etc/yum.repos.d']).exitstatus: + return False + if remote.run(args=['sudo', 'mv', '/etc/yum.repos.d.old', + '/etc/yum.repos.d']).exitstatus: + return False + + +def fix_yum_repos(remote, distro): + """ + For yum calamari installations, the repos.d directory should only + contain a repo file named rhel.repo + """ + if distro.startswith('centos'): + # hack alert: detour: install lttng for ceph + # this works because epel is preinstalled on the vpms + # this is not a generic solution + # this is here solely to test the one-off 1.3.0 release for centos6 + remote.run(args="sudo yum -y install lttng-tools") + cmds = [ + 'sudo mkdir /etc/yum.repos.d.old'.split(), + ['sudo', 'cp', run.Raw('/etc/yum.repos.d/*'), + '/etc/yum.repos.d.old'], + ['sudo', 'rm', run.Raw('/etc/yum.repos.d/epel*')], + ] + for cmd in cmds: + if remote.run(args=cmd).exitstatus: + return False + else: + cmds = [ + 'sudo mv /etc/yum.repos.d /etc/yum.repos.d.old'.split(), + 'sudo mkdir /etc/yum.repos.d'.split(), + ] + for cmd in cmds: + if remote.run(args=cmd).exitstatus: + return False + + # map "distroversion" from Remote.os to a tuple of + # (repo title, repo name descriptor, apt-mirror repo path chunk) + yum_repo_params = { + 'rhel 6.4': ('rhel6-server', 'RHEL', 'rhel6repo-server'), + 'rhel 6.5': ('rhel6-server', 'RHEL', 'rhel6repo-server'), + 'rhel 7.0': ('rhel7-server', 'RHEL', 'rhel7repo/server'), + } + repotitle, reponame, path = yum_repo_params[distro] + repopath = '/etc/yum.repos.d/%s.repo' % repotitle + # TO DO: Make this data configurable too + repo_contents = '\n'.join( + ('[%s]' % repotitle, + 'name=%s $releasever - $basearch' % reponame, + 'baseurl=http://apt-mirror.front.sepia.ceph.com/' + path, + 'gpgcheck=0', + 'enabled=1') + ) + misc.sudo_write_file(remote, repopath, repo_contents) + cmds = [ + 'sudo yum clean all'.split(), + 'sudo yum makecache'.split(), + ] + for cmd in cmds: + if remote.run(args=cmd).exitstatus: + return False + return True + + +@contextlib.contextmanager +def remove_epel(ctx, no_epel): + """ + just remove epel. No undo; assumed that it's used after + adjust_yum_repos, and relies on its state-save/restore. + """ + if no_epel: + for remote in ctx.cluster.remotes: + if remote.os.name.startswith('centos'): + remote.run(args=[ + 'sudo', 'rm', '-f', run.Raw('/etc/yum.repos.d/epel*') + ]) + try: + yield + finally: + pass + + +def get_iceball_with_http(url, destdir): + ''' + Copy iceball with http to destdir. Try both .tar.gz and .iso. + ''' + # stream=True means we don't download until copyfileobj below, + # and don't need a temp file + r = requests.get(url, stream=True) + if not r.ok: + raise RuntimeError("Failed to download %s", str(url)) + filename = os.path.join(destdir, url.split('/')[-1]) + with open(filename, 'w') as f: + shutil.copyfileobj(r.raw, f) + log.info('saved %s as %s' % (url, filename)) + return filename + + +@contextlib.contextmanager +def calamari_install(config, cal_svr): + """ + Install calamari + + The steps here are: + -- Get the iceball, locally or from http + -- Copy the iceball to the calamari server, and untar/mount it. + -- Run ice-setup on the calamari server. + -- Run calamari-ctl initialize. + """ + client_id = str(cal_svr) + at_loc = client_id.find('@') + if at_loc > 0: + client_id = client_id[at_loc + 1:] + + test_image = config['test_image'] + + if not test_image: + raise RuntimeError('Must supply test image') + log.info('calamari test image: %s' % test_image) + delete_iceball = False + + if test_image.startswith('http'): + iceball_file = get_iceball_with_http(test_image, '/tmp') + delete_iceball = True + else: + iceball_file = test_image + + remote_iceball_file = os.path.join('/tmp', os.path.split(iceball_file)[1]) + cal_svr.put_file(iceball_file, remote_iceball_file) + if iceball_file.endswith('.tar.gz'): # XXX specify tar/iso in config? + icetype = 'tarball' + elif iceball_file.endswith('.iso'): + icetype = 'iso' + else: + raise RuntimeError('Can''t handle iceball {0}'.format(iceball_file)) + + if icetype == 'tarball': + ret = cal_svr.run(args=['gunzip', run.Raw('<'), remote_iceball_file, + run.Raw('|'), 'tar', 'xvf', run.Raw('-')]) + if ret.exitstatus: + raise RuntimeError('remote iceball untar failed') + elif icetype == 'iso': + mountpoint = '/mnt/' # XXX create? + ret = cal_svr.run( + args=['sudo', 'mount', '-o', 'loop', '-r', + remote_iceball_file, mountpoint] + ) + + # install ice_setup package + args = { + 'deb': 'sudo dpkg -i /mnt/ice-setup*deb', + 'rpm': 'sudo yum -y localinstall /mnt/ice_setup*rpm' + }.get(cal_svr.system_type, None) + if not args: + raise RuntimeError('{0}: unknown system type'.format(cal_svr)) + ret = cal_svr.run(args=args) + if ret.exitstatus: + raise RuntimeError('ice_setup package install failed') + + # Run ice_setup + icesetdata = 'yes\n\n%s\nhttp\n' % client_id + ice_in = StringIO(icesetdata) + ice_out = StringIO() + if icetype == 'tarball': + args = 'sudo python ice_setup.py' + else: + args = 'sudo ice_setup -d /mnt' + ret = cal_svr.run(args=args, stdin=ice_in, stdout=ice_out) + log.debug(ice_out.getvalue()) + if ret.exitstatus: + raise RuntimeError('ice_setup failed') + + # Run calamari-ctl initialize. + icesetdata = '%s\n%s\n%s\n%s\n' % ( + config['calamari_user'], + config['email'], + config['calamari_password'], + config['calamari_password'], + ) + ice_in = StringIO(icesetdata) + ret = cal_svr.run(args=['sudo', 'calamari-ctl', 'initialize'], + stdin=ice_in, stdout=ice_out) + log.debug(ice_out.getvalue()) + if ret.exitstatus: + raise RuntimeError('calamari-ctl initialize failed') + try: + yield + finally: + log.info('Cleaning up after Calamari installation') + if icetype == 'iso': + cal_svr.run(args=['sudo', 'umount', mountpoint]) + if delete_iceball: + os.unlink(iceball_file) + + +@contextlib.contextmanager +def ceph_install(ctx, cal_svr): + """ + Install ceph if ceph was not previously installed by teuthology. This + code tests the case where calamari is installed on a brand new system. + """ + loc_inst = False + if 'install' not in [x.keys()[0] for x in ctx.config['tasks']]: + loc_inst = True + ret = deploy_ceph(ctx, cal_svr) + if ret: + raise RuntimeError('ceph installs failed') + try: + yield + finally: + if loc_inst: + if not undeploy_ceph(ctx, cal_svr): + log.error('Cleanup of Ceph installed by Calamari-setup failed') + + +def deploy_ceph(ctx, cal_svr): + """ + Perform the ceph-deploy actions needed to bring up a Ceph cluster. This + test is needed to check the ceph-deploy that comes with the calamari + package. + """ + osd_to_name = {} + all_machines = set() + all_mons = set() + all_osds = set() + + # collect which remotes are osds and which are mons + for remote in ctx.cluster.remotes: + all_machines.add(remote.shortname) + roles = ctx.cluster.remotes[remote] + for role in roles: + daemon_type, number = role.split('.') + if daemon_type == 'osd': + all_osds.add(remote.shortname) + osd_to_name[number] = remote.shortname + if daemon_type == 'mon': + all_mons.add(remote.shortname) + + # figure out whether we're in "1.3+" mode: prior to 1.3, there was + # only one Ceph repo, and it was all installed on every Ceph host. + # with 1.3, we've split that into MON and OSD repos (in order to + # be able to separately track subscriptions per-node). This + # requires new switches to ceph-deploy to select which locally-served + # repo is connected to which cluster host. + # + # (TODO: A further issue is that the installation/setup may not have + # created local repos at all, but that is the subject of a future + # change.) + + r = cal_svr.run(args='/usr/bin/test -d /mnt/MON', check_status=False) + use_install_repo = (r.returncode == 0) + + # pre-1.3: + # ceph-deploy new + # ceph-deploy install + # ceph-deploy mon create-initial + # + # 1.3 and later: + # ceph-deploy new + # ceph-deploy install --repo --release=ceph-mon + # ceph-deploy install + # ceph-deploy install --repo --release=ceph-osd + # ceph-deploy install + # ceph-deploy mon create-initial + # + # one might think the install and install + # commands would need --mon and --osd, but #12147 has not yet + # made it into RHCS 1.3.0; since the package split also hasn't + # landed, we can avoid using the flag and avoid the bug. + + cmds = ['ceph-deploy new ' + ' '.join(all_mons)] + + if use_install_repo: + cmds.append('ceph-deploy repo ceph-mon ' + + ' '.join(all_mons)) + cmds.append('ceph-deploy install --no-adjust-repos --mon ' + + ' '.join(all_mons)) + cmds.append('ceph-deploy repo ceph-osd ' + + ' '.join(all_osds)) + cmds.append('ceph-deploy install --no-adjust-repos --osd ' + + ' '.join(all_osds)) + # We tell users to use `hostname` in our docs. Do the same here. + cmds.append('ceph-deploy install --no-adjust-repos --cli `hostname`') + else: + cmds.append('ceph-deploy install ' + ' '.join(all_machines)) + + cmds.append('ceph-deploy mon create-initial') + + for cmd in cmds: + cal_svr.run(args=cmd).exitstatus + + disk_labels = '_dcba' + # NEEDS WORK assumes disks start with vd (need to check this somewhere) + for cmd_pts in [['disk', 'zap'], ['osd', 'prepare'], ['osd', 'activate']]: + mach_osd_cnt = {} + for osdn in osd_to_name: + osd_mac = osd_to_name[osdn] + mach_osd_cnt[osd_mac] = mach_osd_cnt.get(osd_mac, 0) + 1 + arg_list = ['ceph-deploy'] + arg_list.extend(cmd_pts) + disk_id = '%s:vd%s' % (osd_to_name[osdn], + disk_labels[mach_osd_cnt[osd_mac]]) + if 'activate' in cmd_pts: + disk_id += '1' + arg_list.append(disk_id) + cal_svr.run(args=arg_list).exitstatus + + +def undeploy_ceph(ctx, cal_svr): + """ + Cleanup deployment of ceph. + """ + all_machines = [] + ret = True + for remote in ctx.cluster.remotes: + roles = ctx.cluster.remotes[remote] + if ( + not any('osd' in role for role in roles) and + not any('mon' in role for role in roles) + ): + continue + ret &= remote.run( + args=['sudo', 'stop', 'ceph-all', run.Raw('||'), + 'sudo', 'service', 'ceph', 'stop'] + ).exitstatus + all_machines.append(remote.shortname) + all_machines = set(all_machines) + cmd1 = ['ceph-deploy', 'uninstall'] + cmd1.extend(all_machines) + ret &= cal_svr.run(args=cmd1).exitstatus + cmd2 = ['ceph-deploy', 'purge'] + cmd2.extend(all_machines) + ret &= cal_svr.run(args=cmd2).exitstatus + for remote in ctx.cluster.remotes: + ret &= remote.run(args=['sudo', 'rm', '-rf', + '.ssh/known_hosts']).exitstatus + return ret + + +@contextlib.contextmanager +def calamari_connect(ctx, cal_svr): + """ + Connect calamari to the ceph nodes. + """ + connects = ['ceph-deploy', 'calamari', 'connect'] + for machine_info in ctx.cluster.remotes: + if 'client.0' not in ctx.cluster.remotes[machine_info]: + connects.append(machine_info.shortname) + ret = cal_svr.run(args=connects) + if ret.exitstatus: + raise RuntimeError('calamari connect failed') + try: + yield + finally: + log.info('Calamari test terminating') + + +@contextlib.contextmanager +def browser(start_browser, web_page): + """ + Bring up a browser, if wanted. + """ + if start_browser: + webbrowser.open('http://%s' % web_page) + try: + yield + finally: + if start_browser: + log.info('Web browser support terminating')