1 6
import json
2 6
import re
3 6
import io
4 6
import logging
5 6
import os
6 6
import traceback
7 6
import tempfile
8 6
import stat
9 6
import shutil
10 6
try:
11 6
    import psutil
12 0
except Exception:
13 0
    psutil = None
14

15 6
from django.conf import settings
16 6
from cyborgbackup.main.expect import run
17 6
from cyborgbackup.main.models.settings import Setting
18 6
from cyborgbackup.main.utils.encryption import decrypt_field
19

20 6
logger = logging.getLogger('cyborgbackup.main.modules.queriers')
21

22

23 6
class Querier:
24 6
    client = None
25 6
    client_user = 'root'
26

27 6
    def querier(self, module, client, params):
28 0
        try:
29 0
            setting_client_user = Setting.objects.get(key='cyborgbackup_backup_user')
30 0
            self.client_user = setting_client_user.value
31 0
        except Exception:
32 0
            self.client_user = 'root'
33 0
        self.client = client
34 0
        if hasattr(self, 'querier_%s' % module):
35 0
            return getattr(self, 'querier_%s' % module)(params)
36
        else:
37 0
            return {}
38

39 6
    def querier_proxmox(self, args):
40 0
        cmd = ["/usr/bin/pvesh", "get", "/cluster/resources", "--type", "vm", "--output-format=json"]
41 0
        output, rc = self._run(cmd, sudo=True)
42 0
        vms = []
43 0
        if rc == 0:
44 0
            objs = json.loads(output[0])
45 0
            for obj in objs:
46 0
                vms.append({
47
                    'vmid': obj['vmid'],
48
                    'name': obj['name'],
49
                    'type': obj['type'],
50
                    'node': obj['node'],
51
                    'status': obj['status']
52
                })
53 0
            return vms
54
        else:
55 0
            return -1
56

57 6
    def querier_mysql(self, args):
58 0
        cmd = ['mysql', '-NBe', '\'SHOW DATABASES\'']
59
        # cmd = ['echo', '\'\n\nshow databases;\nshow tables;\'', '|', 'mysql']
60 0
        queryargs = {}
61 0
        if 'user' in args.keys():
62 0
            cmd += ['-u{}'.format(args['user'])]
63 0
        if 'password' in args.keys():
64 0
            cmd += ['-p'.format(args['password'])]
65 0
            queryargs['password'] = args['password']
66 0
        if 'port' in args.keys():
67 0
            cmd += ['-P{}'.format(args['port'])]
68 0
        output, rc = self._run(cmd, queryargs=queryargs)
69 0
        dbs = []
70 0
        if rc == 0:
71 0
            for line in output:
72 0
                dbs.append({'name': line})
73 0
            return dbs
74
        else:
75 0
            return -1
76

77 6
    def querier_postgresql(self, args):
78 0
        cmd = ['psql', '-F\',\'', '-t', '-A', '-c', '\'SELECT datname FROM pg_database;\'']
79 0
        if self.client_user == 'root':
80 0
            cmd = ['cd', '/tmp', '&&', 'sudo', '-u', 'postgres'] + cmd
81 0
        output, rc = self._run(cmd)
82 0
        dbs = []
83 0
        if rc == 0:
84 0
            for line in output:
85 0
                dbs.append({'name': line})
86 0
            return dbs
87
        else:
88 0
            return -1
89

90 6
    def _run(self, cmd, sudo=False, queryargs=None):
91 0
        args = []
92 0
        rc = -1
93 0
        finalOutput = []
94

95 0
        if self.client_user != 'root' and sudo:
96 0
            args = ['sudo', '-E']+args
97 0
        args += cmd
98

99 0
        kwargs = {}
100 0
        try:
101 0
            env = {}
102 0
            for attr in dir(settings):
103 0
                if attr == attr.upper() and attr.startswith('CYBORGBACKUP_'):
104 0
                    env[attr] = str(getattr(settings, attr))
105

106 0
            path = tempfile.mkdtemp(prefix='cyborgbackup_module', dir='/tmp/')
107 0
            os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
108 0
            kwargs['private_data_dir'] = path
109

110 0
            if 'private_data_dir' in kwargs.keys():
111 0
                env['PRIVATE_DATA_DIR'] = kwargs['private_data_dir']
112 0
            passwords = {}
113 0
            for setting in Setting.objects.filter(key__contains='ssh_key'):
114 0
                set = Setting.objects.get(key=setting.key.replace('ssh_key', 'ssh_password'))
115 0
                passwords['credential_{}'.format(setting.key)] = decrypt_field(set, 'value')
116 0
            kwargs['passwords'] = passwords
117

118

119 0
            private_data = {'credentials': {}}
120 0
            for sets in Setting.objects.filter(key__contains='ssh_key'):
121
                # If we were sent SSH credentials, decrypt them and send them
122
                # back (they will be written to a temporary file).
123 0
                private_data['credentials'][sets] = decrypt_field(sets, 'value') or ''
124 0
            private_data_files = {'credentials': {}}
125 0
            if private_data is not None:
126 0
                listpaths = []
127 0
                for sets, data in private_data.get('credentials', {}).items():
128
                    # OpenSSH formatted keys must have a trailing newline to be
129
                    # accepted by ssh-add.
130 0
                    if 'OPENSSH PRIVATE KEY' in data and not data.endswith('\n'):
131 0
                        data += '\n'
132
                    # For credentials used with ssh-add, write to a named pipe which
133
                    # will be read then closed, instead of leaving the SSH key on disk.
134 0
                    if sets:
135 0
                        name = 'credential_{}'.format(sets.key)
136 0
                        path = os.path.join(kwargs['private_data_dir'], name)
137 0
                        run.open_fifo_write(path, data)
138 0
                        listpaths.append(path)
139 0
                if len(listpaths) > 1:
140 0
                    private_data_files['credentials']['ssh'] = listpaths
141 0
                elif len(listpaths) == 1:
142 0
                    private_data_files['credentials']['ssh'] = listpaths[0]
143

144 0
            kwargs['private_data_files'] = private_data_files
145

146
            # May have to serialize the value
147 0
            kwargs['private_data_files'] = private_data_files
148 0
            cwd = '/tmp'
149

150 0
            new_args = []
151 0
            new_args += ['ssh', '-Ao', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null']
152 0
            new_args += ['{}@{}'.format(self.client_user, self.client.hostname)]
153 0
            new_args += ['\"echo \'####CYBMOD#####\';', ' '.join(args), '; exitcode=\$?; echo \'####CYBMOD#####\'; exit \$exitcode\"']
154 0
            args = new_args
155

156
            # If there is an SSH key path defined, wrap args with ssh-agent.
157 0
            private_data_files = kwargs.get('private_data_files', {})
158 0
            if 'ssh' in private_data_files.get('credentials', {}):
159 0
                ssh_key_path = private_data_files['credentials']['ssh']
160
            else:
161 0
                ssh_key_path = ''
162 0
            if ssh_key_path:
163 0
                ssh_auth_sock = os.path.join(kwargs['private_data_dir'], 'ssh_auth.sock')
164 0
                args = run.wrap_args_with_ssh_agent(args, ssh_key_path, ssh_auth_sock)
165
            # args = cmd
166

167 0
            expect_passwords = {}
168 0
            d = {}
169

170 0
            for k, v in kwargs['passwords'].items():
171 0
                d[re.compile(r'Enter passphrase for .*' + k + r':\s*?$', re.M)] = k
172 0
                d[re.compile(r'Enter passphrase for .*' + k, re.M)] = k
173 0
            d[re.compile(r'Bad passphrase, try again for .*:\s*?$', re.M)] = ''
174

175 0
            for k, v in d.items():
176 0
                expect_passwords[k] = kwargs['passwords'].get(v, '') or ''
177

178 0
            if queryargs and 'password' in queryargs.keys():
179 0
                expect_passwords[re.compile(r'Enter password: \s*?$', re.M)] = queryargs['password']
180

181 0
            stdout_handle = io.StringIO()
182

183 0
            _kw = dict(
184
                expect_passwords=expect_passwords,
185
                job_timeout=120,
186
                idle_timeout=None,
187
                pexpect_timeout=getattr(settings, 'PEXPECT_TIMEOUT', 5),
188
            )
189 0
            status, rc = run.run_pexpect(
190
                args, cwd, env, stdout_handle, **_kw
191
            )
192 0
            stdout_handle.flush()
193 0
            output = stdout_handle.getvalue().split('\r\n')
194 0
            finalOutput = []
195 0
            start = False
196 0
            for line in output:
197 0
                if 'Enter password: ' in line:
198 0
                    line = line.replace('Enter password: ', '')
199 0
                if line == '####CYBMOD#####' and not start:
200 0
                    start = True
201 0
                if start and line != '####CYBMOD#####' and line != '':
202 0
                    finalOutput += [line]
203

204 0
            shutil.rmtree(kwargs['private_data_dir'])
205 0
        except Exception:
206 0
            tb = traceback.format_exc()
207 0
            if settings.DEBUG:
208 0
                logger.exception('Exception occurred while running task')
209
        finally:
210 0
            try:
211 0
                logger.info('finished running, producing  events.')
212 0
            except Exception:
213 0
                logger.exception('Error flushing stdout and saving event count.')
214 0
        print(rc)
215 0
        print(status)
216 0
        return finalOutput, rc

Read our documentation on viewing source code .

Loading