Ejemplo n.º 1
0
    def get_grains(self):
        client = SSHClient()
        output = client.cmd(tgt='*',
                            fun='grains.item host \
				hwaddr_interfaces ip4_interfaces')
        olist = sorted(output.keys())
        data = {}
        for o in olist:
            if output[o]['retcode'] == 0:
                out = output[o]['return']
                data[out['host']] = output[o]['return']
        return data
Ejemplo n.º 2
0
def salt_ssh(target, command):
    from salt.client.ssh.client import SSHClient
    client = SSHClient(c_path='/etc/salt/master')
    client.opts['ssh_skip_roster'] = True
    client.opts['raw_shell'] = True
    command = client.cmd(tgt=target, fun=command)
    server = next(iter(command))
    retcode =  command[server]['retcode']
    stdout = command[server]['stdout']
    stderr = command[server]['stderr']
    if retcode == 0:
        return stdout,retcode
    else:
        return stderr,retcode
Ejemplo n.º 3
0
def get_ev_files(vpn_ip, path):
    pre_dir = os.path.dirname(path)
    my_capital = os.path.join(pre_dir, "my_capital.config")
    cli = SSHClient()
    try:
        ret = cli.cmd(vpn_ip, "cmd.run", ["cat %s" % my_capital])
        if ret.get(vpn_ip).get("retcode") == 0:
            capital_content = ret.get(vpn_ip).get("return")
            sts = get_config_strategy(capital_content)
            print "sts: ", sts
            return strategy_map_ev(sts)
    except Exception, ex:
        traceback.print_exc()
        Log.error("get ev files error! %s" % ex)
Ejemplo n.º 4
0
    def get_bonding_opts(self, host, bond):
        # maybe tell it where the python lib is?
        client = SSHClient()
        # can't do hosts without a roster file, gotta build that
        # or figure something out
        mcmd = [
            "grep BONDING_OPTS \
			/etc/sysconfig/network-scripts/ifcfg-%s" % bond
        ]
        output = client.cmd(tgt=host, fun='cmd.run', arg=mcmd)
        olist = sorted(output.keys())
        for o in olist:
            if output[o]['retcode'] == 0:
                out = output[o]['return'].lower()
        return out
Ejemplo n.º 5
0
    def get_real_macs(self, host, bond):
        # maybe tell it where the python lib is?
        client = SSHClient()
        # can't do hosts without a roster file, gotta build that
        # or figure something out
        mcmd = ["cat /proc/net/bonding/%s | egrep 'addr|Slave I'" % bond]

        output = client.cmd(tgt=host, fun='cmd.run', arg=mcmd)
        olist = sorted(output.keys())
        realnic = {}
        for o in olist:
            if output[o]['retcode'] == 0:
                out = output[o]['return'].split('\n')
                l = [x.split()[-1] for x in out]
                realmacs = dict(zip(*[iter(l)] * 2))
        return realmacs
Ejemplo n.º 6
0
def push_config(request):
    c = SSHClient()
    if request.is_ajax():
        rst = {}
        regions = Region.objects.all()
        action = request.GET.get('action')

        rst = {}
        for i in regions:
            region = i.region
            roster = ''
            roster_path = './media/salt/region_sls'
            if not os.path.exists(roster_path):
                os.makedirs(roster_path)
            host_list = SaltHost.objects.filter(region=i)
            for h in host_list:
                for u in h.user.all():
                    roster = roster + '''%s-%s:
  host: %s
  port: %s
  user: %s
  passwd: %s
  thin_dir: /home/%s/.salt-thin
  timeout: 30\n''' % (h.ip, u.username, h.ip, h.port, u.username, u.password,
                      u.username)
            with open('%s/roster_%s' % (roster_path, region), 'w') as f:
                f.write(roster)

            if action == 'push':
                r = {}
                ret = c.cmd(tgt='*',
                            fun='state.sls',
                            roster_file='%s/roster_%s' % (roster_path, region),
                            arg=['grains.%s' % region])
                rst = dict(rst, **ret)
            elif action == 'refresh':
                ## refresh grains
                ret = c.cmd(tgt='*',
                            fun='saltutil.sync_grains',
                            roster_file='%s/roster_%s' % (roster_path, region))
                rst = dict(rst, **ret)
            else:
                raise Http404
        return HttpResponse(json.dumps(rst))
Ejemplo n.º 7
0
def job_exec_nginx(host_list, dest_file, bid_list, port, sls, roster_file, desc):
    c = SSHClient()
    data = {'dest_file': dest_file, 'backends': bid_list, 'port': port}
    result_source = c.cmd(tgt=host_list, fun='state.sls', roster_file=roster_file,
                          arg=[sls, 'pillar=%s' % json.dumps(data)], expr_form='list')
    result = []
    keys = result_source.keys()
    keys.sort()
    for i in keys:
        value = result_source[i]
        t_upstream = {}
        t_reload = {}
        ret = 0
        if value['retcode'] != 0:
            ret = 103
        for k, v in value['return'].items():
            keys = k.split('|')
            if keys[-1] == '-replace':
                t_upstream[keys[1]] = {'comment': v['comment'], 'result': v['result']}
            if keys[1] == '-nginx-reload_':
                if v['changes']:
                    if v['changes']['retcode'] != 0:
                        ret = 102 # test failed
                    t_reload = {'comment': v['comment'], 'result': v['result'], 'retcode': v['changes']['retcode'], 'stderr': v['changes']['stderr']}
                else:
                    t_reload = {'comment': v['comment'], 'result': v['result']}
                if v['comment'] == 'onlyif execution failed':
                    ret = 99

        r = {'host': i, 'backends': t_upstream, 'reload': t_reload, 'retcode': value['retcode'], 'ret': ret}
        result.append(r)

        if value['retcode'] == 0 and ret == 0:
            temp = '成功'
        elif ret == 99:
            temp = '失败:配置无任何变更'
        elif ret == 102:
            temp = '失败:配置测试不通过'
        else:
            temp = '失败:未知异常'
        logger.info('{0}nginx服务器{1}后端{2}:{3}{4},返回信息:{5}'.format(desc, i, bid_list, port, temp, r))

    return {'result': result, 'source': result_source}
Ejemplo n.º 8
0
    def __attrs_post_init__(self):
        """Do post init."""
        self._client = SSHClient(c_path=str(self.c_path))

        # if self.roster_file is None:
        #     path = USER_SHARED_PILLAR.all_hosts_path(
        #         'roster.sls'
        #     )
        #     if not path.exists():
        #        path = GLUSTERFS_VOLUME_PILLAR_DIR.all_hosts_path(
        #            'roster.sls'
        #        )

        #    if path.exists():
        #        self.roster_file = path

        if self.roster_file:
            logger.debug(f'default roster is set to {self.roster_file}')
            self._def_roster_data = load_yaml(self.roster_file)
Ejemplo n.º 9
0
def log_tail(request):
    if request.is_ajax():
        pid = request.POST.get('pid')
        host = request.POST.get('host')
        project = Project.objects.get(pk=pid)
        roster_file = os.path.join(
            BASE_DIR, 'media/salt/project_roster/roster_hostgroup_%s' %
            (project.host_group.id))
        data = {'puser': project.host_group.user, 'dpath': project.path}
        sls = 'log_tail'
        c = SSHClient()
        r = c.cmd(tgt=host,
                  fun='state.sls',
                  roster_file=roster_file,
                  arg=[sls, 'pillar=%s' % json.dumps(data)],
                  expr_form='list')
        ret = ''
        for _, v in r.items():
            for _, v1 in v['return'].items():
                ret = v1['changes']['stdout']
        return JsonResponse({'retcode': 0, 'result': ret})
Ejemplo n.º 10
0
    def create_host_files(self):
        client = SSHClient()
        try:
            output = client.cmd(tgt='*', fun='grains.item fqdn_ip4 fqdn host')
        except:
            raise CommandError(self, "Host target is incorrect.")

        f = open('/etc/salt/roster', 'w')
        h = open('/etc/hosts', 'a')
        for o in output:
            if output[o]['retcode'] == 0:
                name = output[o]['return']['host']
                ip = output[o]['return']['fqdn_ip4'][0]
                fqdn = output[o]['return']['fqdn']
                f.write("%s: %s\n" % (name, ip))
                h.write("%s %s %s\n" % (ip, fqdn, name))
            else:
                msg = "Unable to access %s. " % o
                msg += " via ssh. It will not be included."
                print msg

        f.close()
        h.close()
Ejemplo n.º 11
0
def paramiko_ssh(target_list, command):
    res = []
    k = RSAKey.from_private_key_file("/root/.ssh/id_rsa")
    ssh_status = False
    c = SSHClient()
    c.set_missing_host_key_policy(AutoAddPolicy())
    for target in target_list:
        try:
            c.connect( hostname = target, username = "******", pkey = k )
            ssh_status = True
        except:
            return ssh_status, ''
        stdin , stdout, stderr = c.exec_command(command)
        res.append(dict(status=ssh_status, stdout=stdout.read().decode('utf8').strip()))
    return res
Ejemplo n.º 12
0
def project_config(request, template_name, pid=None):
    page_name = u'配置文件'
    content_sls = ''
    content_config = ''

    project = Project.objects.get(pk=pid)
    config_path = './media/salt/config/%s-%s' % (project.id, project.path)
    config_list = [
        i['name']
        for i in ConfigList.objects.filter(project=project).values('name')
    ]
    project_id = (project.path).replace('.', '-')
    roster_file = './media/salt/project_roster/roster_hostgroup_%s' % (
        project.host_group.id)
    # 过滤禁用主机
    host_list = project.host_group.hosts.filter(status=True)
    regions = Region.objects.all()
    rst = {'retcode': 0}
    if request.is_ajax():
        if request.method == 'POST':
            action = request.POST.get('action')
            if action == 'update':
                filename = request.POST.get('config')
                content_config = request.POST.get('content_config')
                content_sls = request.POST.get('content_sls')
                config_path = './media/salt/config/%s-%s' % (project.id,
                                                             project.path)
                file = filename.split('.')[0]
                ## 备份原文件
                shutil.copy('%s/%s.jinja' % (config_path, filename),
                            '%s/%s.jinja.bakup' % (config_path, filename))
                shutil.copy('%s/%s.ini' % (config_path, filename),
                            '%s/%s.ini.bakup' % (config_path, filename))

                try:
                    with open('%s/%s.ini' % (config_path, filename), 'w') as f:
                        f.write(content_sls)
                except:
                    content_config = 'File %s/%s not exists.' % (config_path,
                                                                 filename)

                try:
                    with open('%s/%s.jinja' % (config_path, filename),
                              'w') as f:
                        f.write(content_config)
                except:
                    content_config = 'File %s/%s not exists.' % (config_path,
                                                                 filename)

                return HttpResponse(json.dumps('ok'))

            if action == 'release':
                hosts = request.POST.get('hosts')
                filename = request.POST.get('config')
                #### test ####
                path = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
                # 重命名备份文件
                shutil.copy('%s/%s.jinja.bakup' % (config_path, filename),
                            '%s/%s.jinja.%s' % (config_path, filename, path))
                shutil.copy('%s/%s.ini.bakup' % (config_path, filename),
                            '%s/%s.ini.%s' % (config_path, filename, path))
                c = SSHClient()

                cfg = SomsParse()
                cfg.read(os.path.join(config_path, '{}.ini'.format(filename)))
                data = cfg.as_dict()
                data['puser'] = project.host_group.user
                data['dpath'] = project.path
                data['dtime'] = path
                data['pub_path'] = project.path
                data['filename'] = filename
                data['ctype'] = project.container
                data['project'] = project.id

                rst_source = c.cmd(
                    tgt=hosts,
                    fun='state.sls',
                    roster_file=roster_file,
                    arg=['job_config',
                         'pillar=%s' % json.dumps(data)],
                    expr_form='list')
                #### end #####
                rst = result_handle_config(rst_source)

                # 创建更新列表
                cbak = ConfigBackup()
                cbak.name = ConfigList.objects.filter(project=project).get(
                    name=filename)
                cbak.path = path
                cbak.content = rst
                cbak.save()

                # 记录操作日志
                ConfigLog.objects.create(user=username_auth(request),
                                         project=project.name,
                                         config=filename,
                                         action_ip=user_ip(request),
                                         content=rst,
                                         source_content=rst_source,
                                         msg_type=False)
                return HttpResponse(json.dumps(rst))

            if action == 'rollback':
                hosts = request.POST.get('hosts')
                filename = request.POST.get('config')
                version = request.POST.get('config_ver')
                # 还原对应版本文件
                shutil.copy(
                    '%s/%s.jinja.%s' % (config_path, filename, version),
                    '%s/%s.jinja' % (config_path, filename))
                c = SSHClient()
                data = {
                    'puser': project.host_group.user,
                    'project': project.id,
                    'dpath': project.path,
                    'dtime': version,
                    'pub_path': project.path,
                    'filename': filename,
                    'ctype': project.container
                }
                rst_source = c.cmd(tgt=hosts,
                                   fun='state.sls',
                                   roster_file=roster_file,
                                   arg=[
                                       'job_config_rollback',
                                       'pillar=%s' % json.dumps(data)
                                   ],
                                   expr_form='list')
                #### end #####
                rst = result_handle_config(rst_source)
                ConfigLog.objects.create(user=username_auth(request),
                                         project=project.name,
                                         config=filename,
                                         action_ip=user_ip(request),
                                         content=rst,
                                         source_content=rst_source,
                                         msg_type=True)
                return HttpResponse(json.dumps(rst))

        filename = request.GET.get('config')

        config_path = os.path.join(
            BASE_DIR, './media/salt/config/%s-%s' % (project.id, project.path))
        if not os.path.exists(config_path):
            os.makedirs(config_path)

        file = filename.split('.')[0]
        action = request.GET.get('action')
        if action == 'get':
            try:
                with open('%s/%s.ini' % (config_path, filename), 'r') as f:
                    content_sls = f.read()
            except:
                content_sls = 'File %s/%s not exists, created?' % (config_path,
                                                                   filename)
                rst['retcode'] = 1
            try:
                with open('%s/%s.jinja' % (config_path, filename), 'r') as f:
                    content_config = f.read()
            except:
                content_config = 'File %s/%s not exists, created?' % (
                    config_path, filename)
                rst['retcode'] = 1
        else:
            config = ConfigParser.RawConfigParser()
            region_query = Region.objects.all()
            if len(region_query) == 0:
                rst['retcode'] = 3
                return JsonResponse(rst)
            for i in region_query:
                config.add_section(i.region)
                config.set(i.region, 'name', i.name)
                config.set(i.region, 'region', i.region)
            with open(os.path.join(config_path, '%s.ini' % filename),
                      'w') as f:
                config.write(f)

            try:
                with open('%s/%s.jinja' % (config_path, filename), 'r') as f:
                    content_config = f.read()
            except:
                with open('%s/%s.jinja' % (config_path, filename), 'w') as f:
                    f.write(
                        "{% set region = grains['region'] %}\n#key不存在时报错:pillar[region]['key']\n"
                        +
                        "#key不存在时使用默认值:salt['pillar.get'](region + ':key', 'default')"
                    )
            # 记录文件
            clist = ConfigList()
            clist.name = filename
            clist.project = project
            clist.tag = '%s-%s' % (filename, project.id)
            clist.save()

            rst['retcode'] = 2

        rst['sls'] = content_sls
        rst['config'] = content_config

        return HttpResponse(json.dumps(rst))

    return render(
        request, template_name, {
            'page_name': page_name,
            'pid': pid,
            'all_hosts': host_list,
            'all_regions': regions,
            'project': project,
            'project_id': project_id,
            'config_list': config_list,
            'nav_tag': 'project_list'
        })
Ejemplo n.º 13
0
class SaltSSHClient(SaltClientBase):
    c_path: str = attr.ib(
        default='/etc/salt/master',
        metadata={inputs.METADATA_ARGPARSER: {
            'help': "config path"
        }})
    roster_file: str = attr.ib(
        default=config.SALT_ROSTER_DEFAULT,
        metadata={inputs.METADATA_ARGPARSER: {
            'help': "path to roster file"
        }})
    ssh_options: Optional[Dict] = attr.ib(default=attr.Factory(
        lambda: ['UserKnownHostsFile=/dev/null', 'StrictHostKeyChecking=no']),
                                          metadata={
                                              inputs.METADATA_ARGPARSER: {
                                                  'help':
                                                  "ssh connection options",
                                                  'metavar': 'KEY=VALUE',
                                                  'nargs': '+',
                                                  'action': KeyValueListAction
                                              }
                                          })
    # targets: str = RunArgs.targets
    targets: str = attr.ib(default=config.ALL_MINIONS,
                           metadata={
                               inputs.METADATA_ARGPARSER: {
                                   'help': "command's host targets"
                               }
                           })

    _client: SSHClient = attr.ib(init=False, default=None)
    _def_roster_data: Dict = attr.ib(init=False, default=attr.Factory(dict))

    def __attrs_post_init__(self):
        """Do post init."""
        self._client = SSHClient(c_path=str(self.c_path))

        # if self.roster_file is None:
        #     path = USER_SHARED_PILLAR.all_hosts_path(
        #         'roster.sls'
        #     )
        #     if not path.exists():
        #        path = GLUSTERFS_VOLUME_PILLAR_DIR.all_hosts_path(
        #            'roster.sls'
        #        )

        #    if path.exists():
        #        self.roster_file = path

        if self.roster_file:
            logger.debug(f'default roster is set to {self.roster_file}')
            self._def_roster_data = load_yaml(self.roster_file)

    @property
    def _cmd_args_t(self) -> Type[SaltArgsBase]:
        return SaltSSHArgs

    @property
    def _salt_client_res_t(self) -> Type[SaltClientResultBase]:
        return SaltSSHClientResult

    def roster_data(self, roster_file=None):
        if roster_file:
            return load_yaml(roster_file)
        else:
            return self._def_roster_data

    def roster_targets(self, roster_file=None):
        return list(self.roster_data(roster_file))

    def _run(self, cmd_args: SaltSSHArgs):
        salt_logger = logging.getLogger('salt.client.ssh')
        salt_log_level = None

        if cmd_args.secure and salt_logger.isEnabledFor(logging.DEBUG):
            salt_log_level = salt_logger.getEffectiveLevel()
            salt_logger.setLevel(logging.INFO)

        try:
            return self._client.cmd(*cmd_args.args, **cmd_args.kwargs)
        finally:
            if salt_log_level is not None:
                salt_logger.setLevel(salt_log_level)

    def run(self,
            fun: str,
            fun_args: Union[Tuple, None] = None,
            fun_kwargs: Union[Dict, None] = None,
            secure=False,
            **kwargs):
        for arg in ('roster_file', 'ssh_options', 'targets'):
            arg_v = kwargs.pop(arg, getattr(self, arg))
            if arg_v:
                kwargs[arg] = (str(arg_v) if arg == 'roster_file' else arg_v)

        return super().run(fun, fun_args, fun_kwargs, secure, **kwargs)

    # TODO TEST EOS-8473
    def ensure_access(self, targets: List, bootstrap_roster_file=None):
        for target in targets:
            try:
                # try to reach using default (class-level) settings
                self.run('uname', targets=target, raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                roster_file = exc.cmd_args.get('kw').get('roster_file')

                if roster_file and ('Permission denied' in reason):
                    roster = load_yaml(roster_file)

                    if bootstrap_roster_file:
                        # NOTE assumptions:
                        #   - python3 is already there since it can be
                        #     installed using the bootstrap key
                        #     (so salt modules might be used now)
                        #   - bootstrap key is availble in salt-ssh file roots

                        self.run('state.single',
                                 fun_args=['file.directory'],
                                 fun_kwargs=dict(name='~/.ssh', mode=700),
                                 targets=target,
                                 roster_file=bootstrap_roster_file)

                        # inject production access public key
                        # FIXME hardcoded 'root'
                        self.run(
                            'state.single',
                            fun_args=['ssh_auth.present'],
                            fun_kwargs=dict(
                                name=None,
                                user=roster.get(target,
                                                {}).get('user', 'root'),
                                # FIXME hardcoded path to production pub key
                                source=
                                "salt://provisioner/files/minions/all/id_rsa_prvsnr.pub"  # noqa: E501
                            ),
                            targets=target,
                            roster_file=bootstrap_roster_file)
                    else:
                        copy_id(host=roster.get(target, {}).get('host'),
                                user=roster.get(target, {}).get('user'),
                                port=roster.get(target, {}).get('port'),
                                priv_key_path=roster.get(target,
                                                         {}).get('priv'),
                                ssh_options=exc.cmd_args.get('kw').get(
                                    'ssh_options'),
                                force=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_python3(self, targets: List, roster_file=None):
        for target in targets:
            try:
                self.run('python3 --version',
                         targets=target,
                         roster_file=roster_file,
                         raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                # TODO IMPROVE EOS-8473 better search string / regex
                roster_file = exc.cmd_args.get('kw').get('roster_file')
                if roster_file and ("not found" in reason):
                    self.run(
                        'yum install -y python3',
                        targets=target,
                        roster_file=roster_file,
                        ssh_options=exc.cmd_args.get('kw').get('ssh_options'),
                        raw_shell=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_ready(
        self,
        targets: List,
        bootstrap_roster_file: Optional[Path] = None,
    ):
        if bootstrap_roster_file:
            self.ensure_python3(targets, roster_file=bootstrap_roster_file)

        self.ensure_access(targets,
                           bootstrap_roster_file=bootstrap_roster_file)

        if not bootstrap_roster_file:
            self.ensure_python3(targets)
Ejemplo n.º 14
0
- :mod:`napalm proxy minion <salt.proxy.napalm>`

.. versionadded:: TBD
.. versionchanged:: TBD
'''

from __future__ import absolute_import

import salt.client
import re
import json
import salt.config
from salt.client.ssh.client import SSHClient

client = SSHClient()
local = salt.client.LocalClient()
master = salt.client.Caller()

__virtualname__ = 'nuts'


def __virtual__():
    return __virtualname__


# ----------------------------------------------------------------------------------------------------------------------
# helper functions -- will not be exported
# ----------------------------------------------------------------------------------------------------------------------

Ejemplo n.º 15
0
class SaltSSHClient(SaltClientBase):
    c_path: str = attr.ib(
        default='/etc/salt/master',
        metadata={inputs.METADATA_ARGPARSER: {
            'help': "config path"
        }},
        converter=utils.converter_path_resolved,
    )
    roster_file: str = attr.ib(
        default=config.SALT_ROSTER_DEFAULT,
        metadata={inputs.METADATA_ARGPARSER: {
            'help': "path to roster file"
        }},
        converter=utils.converter_path_resolved,
    )
    profile: str = attr.ib(
        default=None,
        metadata={
            inputs.METADATA_ARGPARSER: {
                'help': ("path to ssh profile, if specified"
                         "'--c-path', '--roster-file', '--pillar-path'"
                         "and '--fileroot-path' options would be set "
                         "automatically")
            }
        },
        converter=utils.converter_path_resolved)
    profile_name: str = attr.ib(default=None,
                                metadata={
                                    inputs.METADATA_ARGPARSER: {
                                        'help':
                                        ("name of the ssh profile. Ignored if"
                                         " '--profile' is specified")
                                    }
                                })
    ssh_options: Optional[Dict] = attr.ib(default=attr.Factory(
        lambda: ['UserKnownHostsFile=/dev/null', 'StrictHostKeyChecking=no']),
                                          metadata={
                                              inputs.METADATA_ARGPARSER: {
                                                  'help':
                                                  "ssh connection options",
                                                  'metavar': 'KEY=VALUE',
                                                  'nargs': '+',
                                                  'action': KeyValueListAction
                                              }
                                          })
    # targets: str = RunArgs.targets
    targets: str = attr.ib(default=config.ALL_MINIONS,
                           metadata={
                               inputs.METADATA_ARGPARSER: {
                                   'help': "command's host targets"
                               }
                           })
    re_config: bool = False

    _client: SSHClient = attr.ib(init=False, default=None)
    _def_roster_data: Dict = attr.ib(init=False, default=attr.Factory(dict))

    def __attrs_post_init__(self):
        """Do post init."""

        if not self.profile and self.profile_name:
            self.profile = (config.profile_base_dir().parent /
                            self.profile_name)

        if self.profile:
            paths = config.profile_paths(
                config.profile_base_dir(profile=self.profile))
            self.c_path = paths['salt_master_file']
            self.roster_file = paths['salt_roster_file']
            self.fileroot_path = converter__fileroot_path(
                paths['salt_fileroot_dir'])
            self.pillar_path = converter__pillar_path(paths['salt_pillar_dir'])

        self._client_init()

        # if self.roster_file is None:
        #     path = USER_SHARED_PILLAR.all_hosts_path(
        #         'roster.sls'
        #     )
        #     if not path.exists():
        #        path = GLUSTERFS_VOLUME_PILLAR_DIR.all_hosts_path(
        #            'roster.sls'
        #        )

        #    if path.exists():
        #        self.roster_file = path

        if self.roster_file:
            logger.debug(f'default roster is set to {self.roster_file}')
            self._def_roster_data = utils.load_yaml(self.roster_file)

    def _client_init(self):
        self._client = SSHClient(c_path=str(self.c_path))

    @property
    def _cmd_args_t(self) -> Type[SaltArgsBase]:
        return SaltSSHArgs

    @property
    def _salt_client_res_t(self) -> Type[SaltClientResultBase]:
        return SaltSSHClientResult

    def _add_file_roots(self, roots: List[Path]):
        config = utils.load_yaml(self.c_path)
        for root in roots:
            if str(root) not in config['file_roots']['base']:
                config['file_roots']['base'].append(str(root))

        utils.dump_yaml(self.c_path, config)

        self._client_init()

    def add_file_roots(self, roots: List[Path]):
        if not self.re_config:
            raise RuntimeError('re-configuration is not allowed')

        return self._add_file_roots(roots)

    def roster_data(self, roster_file=None):
        if roster_file:
            return utils.load_yaml(roster_file)
        else:
            return self._def_roster_data

    def roster_targets(self, roster_file=None):
        return list(self.roster_data(roster_file))

    def roster_nodes(self, roster_file=None):
        return [
            Node(target, params['host'], params['user'], params['post'])
            for target, params in self.roster_data(roster_file).items()
        ]

    def _run(self, cmd_args: SaltSSHArgs):
        salt_logger = logging.getLogger('salt.client.ssh')
        salt_log_level = None

        if cmd_args.secure and salt_logger.isEnabledFor(logging.DEBUG):
            salt_log_level = salt_logger.getEffectiveLevel()
            salt_logger.setLevel(logging.INFO)

        try:
            return self._client.cmd(*cmd_args.args, **cmd_args.kwargs)
        finally:
            if salt_log_level is not None:
                salt_logger.setLevel(salt_log_level)

    def run(self,
            fun: str,
            fun_args: Union[Tuple, None] = None,
            fun_kwargs: Union[Dict, None] = None,
            secure=False,
            **kwargs):
        for arg in ('roster_file', 'ssh_options', 'targets'):
            arg_v = kwargs.pop(arg, None)
            if arg_v is None:
                arg_v = getattr(self, arg)
            if arg_v:
                kwargs[arg] = (str(arg_v) if arg == 'roster_file' else arg_v)

        return super().run(fun, fun_args, fun_kwargs, secure, **kwargs)

    # TODO TEST EOS-8473
    def ensure_access(self,
                      targets: Optional[List] = None,
                      bootstrap_roster_file=None):
        if not targets:
            targets = self.roster_targets()

        for target in targets:
            try:
                # try to reach using default (class-level) settings
                logger.debug(f"Checking access to '{target}'")
                self.run('uname', targets=target, raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                roster_file = exc.cmd_args.get('kw').get('roster_file')

                if roster_file and ('Permission denied' in reason):
                    roster = utils.load_yaml(roster_file)

                    priv_key = Path(roster.get(target, {}).get('priv'))

                    self._add_file_roots([priv_key.parent])

                    logger.debug(f"Copying access key to '{target}'")
                    if bootstrap_roster_file:
                        # NOTE assumptions:
                        #   - python3 is already there since it can be
                        #     installed using the bootstrap key
                        #     (so salt modules might be used now)
                        #   - bootstrap key is availble in salt-ssh file roots
                        #   - access key is availble in salt-ssh file roots

                        self.run('state.single',
                                 fun_args=['file.directory'],
                                 fun_kwargs=dict(name='~/.ssh', mode=700),
                                 targets=target,
                                 roster_file=bootstrap_roster_file)

                        # inject production access public key
                        # FIXME hardcoded 'root'
                        self.run('state.single',
                                 fun_args=['ssh_auth.present'],
                                 fun_kwargs=dict(
                                     name=None,
                                     user=roster.get(target,
                                                     {}).get('user', 'root'),
                                     source=f"salt://{priv_key.name}.pub"),
                                 targets=target,
                                 roster_file=bootstrap_roster_file)
                    else:
                        copy_id(host=roster.get(target, {}).get('host'),
                                user=roster.get(target, {}).get('user'),
                                port=roster.get(target, {}).get('port'),
                                priv_key_path=priv_key,
                                ssh_options=exc.cmd_args.get('kw').get(
                                    'ssh_options'),
                                force=True,
                                target=target)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_python3(self, targets: Optional[List] = None, roster_file=None):
        if not targets:
            targets = self.roster_targets()

        for target in targets:
            logger.debug(f"Ensuring python3 is installed on '{target}'")
            try:
                self.run('python3 --version',
                         targets=target,
                         roster_file=roster_file,
                         raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)

                # TODO IMPROVE EOS-8473 better search string / regex
                roster_file = exc.cmd_args.get('kw').get('roster_file')
                if roster_file and ("not found" in reason):
                    logger.debug(f"Installing python3 on '{target}'")
                    self.run(
                        'yum install -y python3',
                        targets=target,
                        roster_file=roster_file,
                        ssh_options=exc.cmd_args.get('kw').get('ssh_options'),
                        raw_shell=True)
                else:
                    raise

    @staticmethod
    def build_roster(nodes: List[Node], priv_key, roster_path):
        roster = {
            node.minion_id: {
                'host': node.host,
                'user': node.user,
                'port': node.port,
                'priv': str(priv_key)
            }
            for node in nodes
        }
        utils.dump_yaml(roster_path, roster)

    # TODO TEST EOS-8473
    def ensure_ready(
        self,
        targets: Optional[List] = None,
        bootstrap_roster_file: Optional[Path] = None,
    ):
        if not targets:
            targets = self.roster_targets()

        if bootstrap_roster_file:
            self.ensure_python3(targets, roster_file=bootstrap_roster_file)

        self.ensure_access(targets,
                           bootstrap_roster_file=bootstrap_roster_file)

        if not bootstrap_roster_file:
            self.ensure_python3(targets)
Ejemplo n.º 16
0
    def post(self, id):
        username = self.get_secure_cookie('username')
        permission = permissiondict[username]['permission']
        global SALTRET
        global ecount
        SALTRET = 'Just init value'

        if id == 'server_initialization':
            PACKAGE = self.get_argument('package')
            PACKAGE_LINE = [
                i.split() for i in PACKAGE.encode('utf-8').split('\r\n')
            ]
            logger.debug('%s, PACKAGE_LINE: %s', username, PACKAGE_LINE)
            SALTCMD = 'Host Initialization'
            SALT_FUN = 'host.init'

            ecount = 0
            SALTRET = []
            SALTRET.append('')
            ## 需要加入主机名和IP地址不重复验证
            ## 日后再加
            for ELMENT in PACKAGE_LINE:
                j = ' '.join(ELMENT)
                if len(ELMENT) < 2:
                    ecount += 1
                    SALTRET.append({j: 1})
                else:
                    SALTRET.append({j: 0})
            logger.debug('%s, ecount: %s SALTRET: %s', username, ecount,
                         SALTRET)
            if ecount > 0:
                SALTRET[0] = '下列标红的行所提供之信息不完整,请修正后重新提交: '
                self.render('result.html',
                            SALTCOMMAND=SALTCMD,
                            ECOUNT=ecount,
                            SALTRESULT=SALTRET,
                            FLAGID=id,
                            MENUDICT=menudict,
                            SALTFUNCTION=SALT_FUN,
                            PERMISSION=permission)

            else:
                ret_usertype = 0
                PACKAGE_DICT = {}
                HOSTNAME_DICT = {}
                ROSTER_CONF = '.roster_' + str(time.time())
                for USER in ['root', 'ubuntu']:
                    if USER == 'root':
                        for ELMENT in PACKAGE_LINE:
                            if len(ELMENT) == 3:
                                PASS = ELMENT[-1]
                            else:
                                PASS = '******'
                            PACKAGE_DICT[ELMENT[1]] = {
                                'host': ELMENT[0],
                                'user': USER,
                                'passwd': PASS,
                                'port': 22
                            }
                            HOSTNAME_DICT[ELMENT[0]] = ELMENT[1]
                            PACKAGE_YAML = yaml.dump(PACKAGE_DICT)
                            logger.debug('%s, PACKAGE_YAML: %s', username,
                                         PACKAGE_YAML)
                            ROSTER_FD = open(ROSTER_CONF, 'w')
                            ROSTER_FD.write(PACKAGE_YAML)
                            ROSTER_FD.close()
                    elif USER == 'ubuntu':
                        for hosty in retb:
                            if retb[hosty] == 0:
                                PACKAGE_DICT.pop(hosty)
                            elif retb[hosty] == 1:
                                PACKAGE_DICT[hosty]['user'] = '******'

                    logger.debug('%s, PACKAGE_DICT: %s', username,
                                 PACKAGE_DICT)
                    TARGET = ','.join([i for i in HOSTNAME_DICT.values()])

                    ## 验证ssh的用户密码是否正确
                    SALTSSH_RETFILE = '.saltsshret_' + str(time.time())

                    retb = LoginVirifi(PACKAGE_DICT)
                    logger.debug('%s, The result of LoginVirifi: %s', username,
                                 retb)
                    retc = sum(retb.values())
                    if retc == 0:
                        ret_usertype = ret_usertype - 1
                        logger.debug(
                            '%s, All host LoginVirifi success,ret_usertype: %s',
                            username, ret_usertype)
                        break
                    else:
                        ret_usertype = 1
                        logger.debug(
                            '%s, All or part of host LoginVirifi fail,ret_usertype: %s',
                            username, ret_usertype)
                        continue

                ## 验证用户为ubuntu时,修改root密码与ubuntu用户密码相同
                ## ubuntu 用户修改root 密码失败暂未做处理
                if ret_usertype == 1:
                    ecount = -1
                    SALTRET = []
                    SALTRET.append('下列标红的服务器ssh登录失败,请修正后重新提交:')
                    for j in PACKAGE_LINE:
                        k = ' '.join(j)
                        if j[1] in retb.keys():
                            SALTRET.append({k: 1})
                        else:
                            SALTRET.append({k: 0})
                    logger.info('%s, ecount: %s SALTRET: %s', username, ecount,
                                SALTRET)
                    self.render('result.html',
                                SALTCOMMAND=SALTCMD,
                                ECOUNT=ecount,
                                SALTRESULT=SALTRET,
                                FLAGID=id,
                                MENUDICT=menudict,
                                SALTFUNCTION=SALT_FUN,
                                PERMISSION=permission)
                else:
                    #SALT_FUN = 'state.sls'
                    self.render('result.html',
                                SALTCOMMAND=SALTCMD,
                                ECOUNT=ecount,
                                SALTRESULT=SALTRET,
                                FLAGID=id,
                                MENUDICT=menudict,
                                SALTFUNCTION=SALT_FUN,
                                PERMISSION=permission)

                    ## 验证用户为ubuntu时,修改root密码与ubuntu用户密码相同
                    ## ubuntu 用户修改root 密码失败暂未做处理
                    if ret_usertype == 0:
                        retd = ChangePasswd(PACKAGE_DICT)
                        logger.debug('%s, The result of ChangePasswd: %s',
                                     username, retd)
                        rete = sum(retd.values())

    ## host init
                    client = SSHClient()
                    logger.debug(
                        "%s, client.cmd\(tgt=%s,fun='state.sls', arg=['inithost'],roster_file=%s,expr_form=\'list\',kwarg={'pillar':%s,}\)",
                        username, TARGET, ROSTER_CONF, HOSTNAME_DICT)
                    # rand_thin_dir=True or -W is for fixing the salt-ssh problem when minion is python2.7 and master is python2.6 can cause error below:
                    # 'AttributeError: 'module' object has no attribute 'fromstringlist
                    # refer https://github.com/saltstack/salt/issues/26584
                    RET = client.cmd(tgt=TARGET,
                                     fun='state.sls',
                                     arg=['inithost'],
                                     roster_file=ROSTER_CONF,
                                     expr_form='list',
                                     ignore_host_keys=True,
                                     rand_thin_dir=True,
                                     kwarg={'pillar': HOSTNAME_DICT})
                    logger.debug('%s, ecount: %d RET: %s', username, ecount,
                                 RET)
                    SALTRET = ret_process(RET, dtype='init')
                    logger.info('%s, SALTRET: %s', username, SALTRET)
Ejemplo n.º 17
0
 def __attrs_post_init__(self):
     self._client = SSHClient(c_path=str(self.c_path))
Ejemplo n.º 18
0
class SaltSSHClient(SaltClientBase):
    roster_file: str = None
    ssh_options: Optional[Dict] = None

    _client: SSHClient = attr.ib(init=False, default=None)

    def __attrs_post_init__(self):
        self._client = SSHClient(c_path=str(self.c_path))

    @property
    def _cmd_args_t(self) -> Type[SaltClientArgsBase]:
        return SaltSSHArgs

    @property
    def _salt_client_res_t(self) -> Type[SaltClientResult]:
        return SaltSSHClientResult

    def _run(self, cmd_args: SaltSSHArgs):
        salt_logger = logging.getLogger('salt.client.ssh')
        salt_log_level = None

        if cmd_args.secure and salt_logger.isEnabledFor(logging.DEBUG):
            salt_log_level = salt_logger.getEffectiveLevel()
            salt_logger.setLevel(logging.INFO)

        try:
            return self._client.cmd(*cmd_args.args, **cmd_args.kwargs)
        finally:
            if salt_log_level is not None:
                salt_logger.setLevel(salt_log_level)

    # TODO TEST EOS-8473
    def ensure_access(self, targets: List, bootstrap_roster_file=None):
        for target in targets:
            try:
                # try to reach using default (class-level) settings
                self.run('uname', targets=target, raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                roster_file = exc.cmd_args.get('kw').get('roster_file')

                if roster_file and ('Permission denied' in reason):
                    roster = load_yaml(roster_file)

                    if bootstrap_roster_file:
                        # NOTE assumptions:
                        #   - python3 is already there since it can be
                        #     installed using the bootstrap key
                        #     (so salt modules might be used now)
                        #   - bootstrap key is availble in salt-ssh file roots

                        self.run('state.single',
                                 fun_args=['file.directory'],
                                 fun_kwargs=dict(name='~/.ssh', mode=700),
                                 targets=target,
                                 roster_file=bootstrap_roster_file)

                        # inject production access public key
                        # FIXME hardcoded 'root'
                        self.run(
                            'state.single',
                            fun_args=['ssh_auth.present'],
                            fun_kwargs=dict(
                                name=None,
                                user=roster.get(target,
                                                {}).get('user', 'root'),
                                # FIXME hardcoded path to production pub key
                                source=
                                "salt://provisioner/files/minions/all/id_rsa_prvsnr.pub"  # noqa: E501
                            ),
                            targets=target,
                            roster_file=bootstrap_roster_file)
                    else:
                        copy_id(host=roster.get(target, {}).get('host'),
                                user=roster.get(target, {}).get('user'),
                                port=roster.get(target, {}).get('port'),
                                priv_key_path=roster.get(target,
                                                         {}).get('priv'),
                                ssh_options=exc.cmd_args.get('kw').get(
                                    'ssh_options'),
                                force=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_python3(self, targets: List, roster_file=None):
        for target in targets:
            try:
                self.run('python3 --version',
                         targets=target,
                         roster_file=roster_file,
                         raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                # TODO IMPROVE EOS-8473 better search string / regex
                roster_file = exc.cmd_args.get('kw').get('roster_file')
                if roster_file and ("not found" in reason):
                    self.run(
                        'yum install -y python3',
                        targets=target,
                        roster_file=roster_file,
                        ssh_options=exc.cmd_args.get('kw').get('ssh_options'),
                        raw_shell=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_ready(
        self,
        targets: List,
        bootstrap_roster_file: Optional[Path] = None,
    ):
        if bootstrap_roster_file:
            self.ensure_python3(targets, roster_file=bootstrap_roster_file)

        self.ensure_access(targets,
                           bootstrap_roster_file=bootstrap_roster_file)

        if not bootstrap_roster_file:
            self.ensure_python3(targets)

    # FIXME issues:
    #   1. not properly processed error, e.g.:
    #
    #        self.run(
    #            'state.single',
    #            fun_args=['ssh_auth.present'],
    #            fun_kwargs=dict(
    #                user=roster.get(target, {}).get(
    #                    'user', 'root'
    #                ),
    #                source=f"{priv_file}.pub"
    #            ),
    #            targets=target,
    #            roster_file=bootstrap_roster_file
    #        )
    #
    #        will return with success but salt actually error takes place:
    #
    #        TypeError encountered executing state.single: single() missing 1
    #        required positional argument: 'name'

    # TODO TYPE EOS-8473
    def run(self, *args, roster_file=None, ssh_options=None, **kwargs):
        if roster_file is None:
            roster_file = self.roster_file
        if roster_file:
            kwargs['roster_file'] = str(roster_file)
        if ssh_options is None:
            ssh_options = self.ssh_options
        if ssh_options:
            kwargs['ssh_options'] = ssh_options
        return super().run(*args, **kwargs)
Ejemplo n.º 19
0
 def _client_init(self):
     self._client = SSHClient(c_path=str(self.c_path))
Ejemplo n.º 20
0
def process_info(project, hosts):
    roster_file = os.path.join(
        BASE_DIR, 'media/salt/project_roster/roster_hostgroup_%s' %
        (project.host_group.id))
    c = SSHClient()
    if project.container == 0:
        ## 显示长用户名
        rst = c.cmd(
            hosts,
            'cmd.run', [
                'ps -o ruser=LONGUSERNAME12 -eo pid,ppid,pcpu,pmem,rss,lstart,etime,cmd|grep config.file=/home/%s/%s/conf|grep -v grep|awk \'{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$12}\''
                % (project.host_group.user, project.path)
            ],
            roster_file=roster_file,
            expr_form='list')

    else:
        logger.info('Process Collect')
        rst = c.cmd(
            hosts,
            'cmd.run', [
                'if [ -f /home/%s/%s/RUNNING_PID ];then ps -o ruser=LONGUSERNAME12 -eo pid,ppid,pcpu,pmem,rss,lstart,etime,cmd|grep `cat /home/%s/%s/RUNNING_PID`|grep -v grep|awk \'{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$12}\';else exit 1;fi'
                % (project.host_group.user, project.path,
                   project.host_group.user, project.path)
            ],
            roster_file=roster_file,
            expr_form='list')
    logger.info('Result: %s' % rst)
    result = {}
    for k, v in rst.items():
        try:
            plist = ProcessList.objects.get(tag='%s-%s' % (project.id, k))
        except:
            plist = ProcessList()
            plist.tag = '%s-%s' % (project.id, k)
        plist.project = project
        plist.host = SaltHost.objects.get(ip=k)
        if v['retcode'] == 0 and v['return']:
            rlist = v['return'].split(' ')
            t = 0
            if "-" in rlist[9]:
                t = t + int(rlist[9].split("-")[0]) * 24 * 3600
                d = rlist[9].split("-")[1]
            else:
                d = rlist[9].split("-")[0]
            d = d.split(":")
            if len(d) == 3:
                t = t + int(d[0]) * 3600 + int(d[1]) * 60 + int(d[2])
            else:
                t = t + int(d[0]) * 60 + int(d[1])
            plist.process_user = rlist[0]
            plist.process_pid = rlist[1]
            plist.process_ppid = rlist[2]
            plist.process_cpu_per = rlist[3]
            plist.process_mem_per = rlist[4]
            plist.process_rmem = rlist[5]
            plist.process_start = '%s%s-%s' % (rlist[6], rlist[7], rlist[8])
            plist.process_etime = t
            r = "%s %s %s %s %s %s %s%s-%s %s" % (
                rlist[0], rlist[1], rlist[2], rlist[3], rlist[4], rlist[5],
                rlist[6], rlist[7], rlist[8], t)
        else:
            r = "None None None None None None None None"
            plist.process_user = None
            plist.process_pid = None
            plist.process_ppid = None
            plist.process_cpu_per = None
            plist.process_mem_per = None
            plist.process_rmem = None
            plist.process_start = None
            plist.process_etime = None
        plist.save()
        plist = ProcessList.objects.get(tag='%s-%s' % (project.id, k))
        result[plist.pk] = r

    return result
Ejemplo n.º 21
0
class SaltSSHClient(SaltClientBase):
    roster_file: str = None
    ssh_options: Optional[Dict] = None

    _client: SSHClient = attr.ib(init=False, default=None)

    def __attrs_post_init__(self):
        self._client = SSHClient(c_path=str(self.c_path))

    @property
    def _cmd_args_t(self) -> Type[SaltClientArgsBase]:
        return SaltSSHArgs

    @property
    def _salt_client_res_t(self) -> Type[SaltClientResult]:
        return SaltSSHClientResult

    def _run(self, cmd_args: SaltSSHArgs):
        return self._client.cmd(*cmd_args.args, **cmd_args.kwargs)

    # TODO TEST EOS-8473
    def ensure_access(self, targets: List, roster_file=None, ssh_options=None):
        for target in targets:
            try:
                self.run('uname',
                         targets=target,
                         roster_file=roster_file,
                         ssh_options=ssh_options,
                         raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                roster_file = exc.cmd_args.get('kw').get('roster_file')
                if roster_file and ('Permission denied' in reason):
                    roster = load_yaml(roster_file)
                    copy_id(
                        host=roster.get(target, {}).get('host'),
                        user=roster.get(target, {}).get('user'),
                        port=roster.get(target, {}).get('port'),
                        priv_key_path=roster.get(target, {}).get('priv'),
                        ssh_options=exc.cmd_args.get('kw').get('ssh_options'),
                        force=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_python3(self,
                       targets: List,
                       roster_file=None,
                       ssh_options=None):
        for target in targets:
            try:
                self.run('python3 --version',
                         targets=target,
                         roster_file=roster_file,
                         ssh_options=ssh_options,
                         raw_shell=True)
            except SaltCmdResultError as exc:
                reason = exc.reason.get(target)
                # TODO IMPROVE EOS-8473 better search string / regex
                roster_file = exc.cmd_args.get('kw').get('roster_file')
                if roster_file and ("not found" in reason):
                    self.run(
                        'yum install -y python3',
                        targets=target,
                        roster_file=exc.cmd_args.get('kw').get('roster_file'),
                        ssh_options=exc.cmd_args.get('kw').get('ssh_options'),
                        raw_shell=True)
                else:
                    raise

    # TODO TEST EOS-8473
    def ensure_ready(self, targets: List, roster_file=None, ssh_options=None):
        self.ensure_access(targets,
                           roster_file=roster_file,
                           ssh_options=ssh_options)

        self.ensure_python3(targets,
                            roster_file=roster_file,
                            ssh_options=ssh_options)

    # TODO TYPE EOS-8473
    def run(self, *args, roster_file=None, ssh_options=None, **kwargs):
        if roster_file is None:
            roster_file = self.roster_file
        if roster_file:
            kwargs['roster_file'] = str(roster_file)
        if ssh_options is None:
            ssh_options = self.ssh_options
        if ssh_options:
            kwargs['ssh_options'] = ssh_options
        return super().run(*args, **kwargs)