def __init__(self, middleware, method_name, serviceobj, method, args, options, pipes): self._finished = asyncio.Event() self.middleware = middleware self.method_name = method_name self.serviceobj = serviceobj self.method = method self.args = args self.options = options self.pipes = pipes or Pipes(input=None, output=None) self.id = None self.lock = None self.result = None self.error = None self.exception = None self.exc_info = None self.state = State.WAITING self.progress = { 'percent': None, 'description': None, 'extra': None, } self.time_started = datetime.now() self.time_finished = None self.loop = asyncio.get_event_loop() self.future = None self.logs_path = None self.logs_fd = None self.logs_excerpt = None if self.options["check_pipes"]: for pipe in self.options["pipes"]: self.check_pipe(pipe)
async def do_create(self, job, options): """ Create a new database entry with identifier as the tag, all entries are lowercased Then puts the file in the /var/db/system/webui/images directory """ identifier = options.get('identifier') self.__ensure_dir() try: id = await self.middleware.call('datastore.insert', 'system.filesystem', {'identifier': identifier.lower()}) except IntegrityError as e: # Likely a duplicate entry raise CallError(e) final_location = f"/var/db/system/webui/images/{id}.png" put_job = await self.middleware.call( 'filesystem.put', final_location, {"mode": 0o755}, pipes=Pipes(input=job.pipes.input)) await put_job.wait() return id
async def download(self, method, args, filename): """ Core helper to call a job marked for download. Returns the job id and the URL for download. """ job = await self.middleware.call(method, *args, pipes=Pipes(output=self.middleware.pipe())) token = await self.middleware.call('auth.generate_token', 300, {'filename': filename, 'job': job.id}) self.middleware.fileapp.register_job(job.id) return job.id, f'/_download/{job.id}?auth_token={token}'
def __init__(self, middleware, method_name, serviceobj, method, args, options, pipes, on_progress_cb): self._finished = asyncio.Event(loop=middleware.loop) self.middleware = middleware self.method_name = method_name self.serviceobj = serviceobj self.method = method self.args = args self.options = options self.pipes = pipes or Pipes(input=None, output=None) self.on_progress_cb = on_progress_cb self.id = None self.lock = None self.result = None self.error = None self.exception = None self.exc_info = None self.aborted = False self.state = State.WAITING self.description = None self.progress = { 'percent': None, 'description': None, 'extra': None, } self.internal_data = {} self.time_started = datetime.utcnow() self.time_finished = None self.loop = self.middleware.loop self.future = None self.logs_path = None self.logs_fd = None self.logs_excerpt = None if self.options["check_pipes"]: for pipe in self.options["pipes"]: self.check_pipe(pipe) if self.options["description"]: try: self.description = self.options["description"](*args) except Exception: logger.error("Error setting job description", exc_info=True)
async def new_ticket(self, job, data): """ Creates a new ticket for support. This is done using the support proxy API. For FreeNAS it will be created on Redmine and for TrueNAS on SupportSuite. For FreeNAS `criticality`, `environment`, `phone`, `name` and `email` attributes are not required. For TrueNAS `username`, `password` and `type` attributes are not required. """ await self.middleware.call('network.general.will_perform_activity', 'support') job.set_progress(1, 'Gathering data') sw_name = 'freenas' if not await self.middleware.call('system.is_enterprise') else 'truenas' if sw_name == 'freenas': required_attrs = ('type', 'username', 'password') else: required_attrs = ('phone', 'name', 'email', 'criticality', 'environment') data['serial'] = (await self.middleware.call('system.dmidecode_info'))['system-serial-number'] license = (await self.middleware.call('system.info'))['license'] if license: data['company'] = license['customer_name'] else: data['company'] = 'Unknown' for i in required_attrs: if i not in data: raise CallError(f'{i} is required', errno.EINVAL) data['version'] = (await self.middleware.call('system.version')).split('-', 1)[-1] if 'username' in data: data['user'] = data.pop('username') debug = data.pop('attach_debug') type_ = data.get('type') if type_: data['type'] = type_.lower() job.set_progress(20, 'Submitting ticket') try: r = await self.middleware.run_in_thread(lambda: requests.post( f'https://{ADDRESS}/{sw_name}/api/v1.0/ticket', data=json.dumps(data), headers={'Content-Type': 'application/json'}, timeout=10, )) result = r.json() except simplejson.JSONDecodeError: self.logger.debug(f'Failed to decode ticket attachment response: {r.text}') raise CallError('Invalid proxy server response', errno.EBADMSG) except requests.ConnectionError as e: raise CallError(f'Connection error {e}', errno.EBADF) except requests.Timeout: raise CallError('Connection time out', errno.ETIMEDOUT) if r.status_code != 200: self.logger.debug(f'Support Ticket failed ({r.status_code}): {r.text}', r.status_code, r.text) raise CallError('Ticket creation failed, try again later.', errno.EINVAL) if result['error']: raise CallError(result['message'], errno.EINVAL) ticket = result.get('ticketnum') url = result.get('message') if not ticket: raise CallError('New ticket number was not informed', errno.EINVAL) job.set_progress(50, f'Ticket created: {ticket}', extra={'ticket': ticket}) if debug: job.set_progress(60, 'Generating debug file') debug_job = await self.middleware.call( 'system.debug', pipes=Pipes(output=self.middleware.pipe()), ) if await self.middleware.call('system.is_enterprise') and await self.middleware.call('failover.licensed'): debug_name = 'debug-{}.tar'.format(time.strftime('%Y%m%d%H%M%S')) else: debug_name = 'debug-{}-{}.txz'.format( socket.gethostname().split('.')[0], time.strftime('%Y%m%d%H%M%S'), ) job.set_progress(80, 'Attaching debug file') t = { 'ticket': ticket, 'filename': debug_name, } if 'user' in data: t['username'] = data['user'] if 'password' in data: t['password'] = data['password'] tjob = await self.middleware.call( 'support.attach_ticket', t, pipes=Pipes(input=self.middleware.pipe()), ) def copy(): try: rbytes = 0 while True: r = debug_job.pipes.output.r.read(1048576) if r == b'': break rbytes += len(r) if rbytes > 20971520: raise CallError('Debug too large to attach', errno.EFBIG) tjob.pipes.input.w.write(r) finally: tjob.pipes.input.w.close() await self.middleware.run_in_thread(copy) await debug_job.wait() await tjob.wait() else: job.set_progress(100) return { 'ticket': ticket, 'url': url, }
async def new_ticket(self, job, data): """ Creates a new ticket for support. This is done using the support proxy API. For FreeNAS it will be created on Redmine and for TrueNAS on SupportSuite. For FreeNAS `criticality`, `environment`, `phone`, `name` and `email` attributes are not required. For TrueNAS `username`, `password` and `type` attributes are not required. """ job.set_progress(1, 'Gathering data') sw_name = 'freenas' if await self.middleware.call('system.is_freenas' ) else 'truenas' if sw_name == 'freenas': required_attrs = ('type', 'username', 'password') else: required_attrs = ('phone', 'name', 'email', 'criticality', 'environment') data['serial'] = (await (await Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE)).communicate() )[0].decode().split('\n')[0].upper() license = get_license()[0] if license: data['company'] = license.customer_name else: data['company'] = 'Unknown' for i in required_attrs: if i not in data: raise CallError(f'{i} is required', errno.EINVAL) data['version'] = (await self.middleware.call('system.version')).split( '-', 1)[-1] if 'username' in data: data['user'] = data.pop('username') debug = data.pop('attach_debug') type_ = data.get('type') if type_: data['type'] = type_.lower() job.set_progress(20, 'Submitting ticket') try: r = await self.middleware.run_in_thread(lambda: requests.post( f'https://{ADDRESS}/{sw_name}/api/v1.0/ticket', data=json.dumps(data), headers={'Content-Type': 'application/json'}, timeout=10, )) result = r.json() except simplejson.JSONDecodeError: self.logger.debug( f'Failed to decode ticket attachment response: {r.text}') raise CallError('Invalid proxy server response', errno.EBADMSG) except requests.ConnectionError as e: raise CallError(f'Connection error {e}', errno.EBADF) except requests.Timeout: raise CallError('Connection time out', errno.ETIMEDOUT) if r.status_code != 200: self.logger.debug( f'Support Ticket failed ({r.status_code}): {r.text}', r.status_code, r.text) raise CallError('Ticket creation failed, try again later.', errno.EINVAL) if result['error']: raise CallError(result['message'], errno.EINVAL) ticket = result.get('ticketnum') url = result.get('message') if not ticket: raise CallError('New ticket number was not informed', errno.EINVAL) job.set_progress(50, f'Ticket created: {ticket}', extra={'ticket': ticket}) if debug: # FIXME: generate debug from middleware mntpt, direc, dump = await self.middleware.run_in_thread( debug_get_settings) job.set_progress(60, 'Generating debug file') await self.middleware.run_in_thread(debug_generate) not_freenas = not (await self.middleware.call('system.is_freenas')) if not_freenas: not_freenas &= await self.middleware.call( 'notifier.failover_licensed') if not_freenas: debug_file = f'{direc}/debug.tar' debug_name = 'debug-{}.tar'.format( time.strftime('%Y%m%d%H%M%S')) else: debug_file = dump debug_name = 'debug-{}-{}.txz'.format( socket.gethostname().split('.')[0], time.strftime('%Y%m%d%H%M%S'), ) job.set_progress(80, 'Attaching debug file') t = { 'ticket': ticket, 'filename': debug_name, } if 'user' in data: t['username'] = data['user'] if 'password' in data: t['password'] = data['password'] tjob = await self.middleware.call( 'support.attach_ticket', t, pipes=Pipes(input=self.middleware.pipe())) with open(debug_file, 'rb') as f: await self.middleware.run_in_io_thread(shutil.copyfileobj, f, tjob.pipes.input.w) await self.middleware.run_in_io_thread(tjob.pipes.input.w.close ) await tjob.wait() else: job.set_progress(100) return { 'ticket': ticket, 'url': url, }
async def new_ticket(self, job, data): """ Creates a new ticket for support. This is done using the support proxy API. For TrueNAS SCALE it will be created on JIRA and for TrueNAS SCALE Enterprise on Salesforce. For SCALE `criticality`, `environment`, `phone`, `name` and `email` attributes are not required. For SCALE Enterprise `token` and `type` attributes are not required. """ await self.middleware.call('network.general.will_perform_activity', 'support') job.set_progress(1, 'Gathering data') sw_name = 'freenas' if not await self.middleware.call('system.is_enterprise') else 'truenas' if sw_name == 'freenas': required_attrs = ('type', 'token') else: required_attrs = ('phone', 'name', 'email', 'criticality', 'environment') data['serial'] = (await self.middleware.call('system.dmidecode_info'))['system-serial-number'] license = await self.middleware.call('system.license') if license: data['company'] = license['customer_name'] else: data['company'] = 'Unknown' for i in required_attrs: if i not in data: raise CallError(f'{i} is required', errno.EINVAL) data['version'] = (await self.middleware.call('system.version')).split('-', 1)[-1] debug = data.pop('attach_debug') type_ = data.get('type') if type_: data['type'] = type_.lower() job.set_progress(20, 'Submitting ticket') result = await post( f'https://{ADDRESS}/{sw_name}/api/v1.0/ticket', data=json.dumps(data), ) if result['error']: raise CallError(result['message'], errno.EINVAL) ticket = result.get('ticketnum') url = result.get('message') if not ticket: raise CallError('New ticket number was not informed', errno.EINVAL) job.set_progress(50, f'Ticket created: {ticket}', extra={'ticket': ticket}) has_debug = False if debug: job.set_progress(60, 'Generating debug file') debug_job = await self.middleware.call( 'system.debug', pipes=Pipes(output=self.middleware.pipe()), ) if await self.middleware.call('failover.licensed'): debug_name = 'debug-{}.tar'.format(time.strftime('%Y%m%d%H%M%S')) else: debug_name = 'debug-{}-{}.txz'.format( (await self.middleware.call('system.hostname')).split('.')[0], time.strftime('%Y%m%d%H%M%S'), ) with tempfile.NamedTemporaryFile("w+b") as f: def copy1(): nonlocal has_debug try: rbytes = 0 while True: r = debug_job.pipes.output.r.read(1048576) if r == b'': break rbytes += len(r) if rbytes > DEBUG_MAX_SIZE * 1048576: return f.write(r) f.seek(0) has_debug = True finally: debug_job.pipes.output.r.read() await self.middleware.run_in_thread(copy1) await debug_job.wait() if has_debug: job.set_progress(80, 'Attaching debug file') t = { 'ticket': ticket, 'filename': debug_name, } if 'token' in data: t['token'] = data['token'] tjob = await self.middleware.call( 'support.attach_ticket', t, pipes=Pipes(input=self.middleware.pipe()), ) def copy2(): try: shutil.copyfileobj(f, tjob.pipes.input.w) finally: tjob.pipes.input.w.close() await self.middleware.run_in_thread(copy2) await tjob.wait() else: job.set_progress(100) return { 'ticket': ticket, 'url': url, 'has_debug': has_debug, }