def index(request): sw_name = get_sw_name().lower() license, reason = utils.get_license() allow_update = True if hasattr(notifier, 'failover_status'): status = notifier().failover_status() if status not in ('MASTER', 'SINGLE'): allow_update = False context = { 'sw_name': sw_name, 'license': license, 'fc_enabled': utils.fc_enabled(), 'allow_update': allow_update, } for c in appPool.hook_view_context('support.index', request): context.update(c) if not notifier().is_freenas(): form = forms.ProductionForm() if request.method == 'POST': form = forms.ProductionForm(request.POST) if form.is_valid(): form.save() return JsonResp( request, message='Production status successfully updated.' ) context['production_form'] = form return render(request, 'support/home.html', context)
def is_available(cls, support=None): """ Checks whether the Proactive Support tab should be shown. It should only be for TrueNAS and Siver/Gold Customers. Returns: tuple(bool, Support instance) """ if notifier().is_freenas(): return False, support if support is None: try: support = cls.objects.order_by('-id')[0] except IndexError: support = cls.objects.create() license, error = get_license() if license is None: return False, support if license.contract_type in ( ContractType.silver, ContractType.gold, ): return True, support return False, support
def license_update(request): license, reason = utils.get_license() if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, 'wb+') as f: f.write(form.cleaned_data.get('license').encode('ascii')) try: _n = notifier() if hasattr(_n, 'failover_getpeer'): ip, secret = _n.failover_getpeer() if ip: s = _n.failover_rpc(ip=ip) _n.sync_file_send(s, secret, utils.LICENSE_FILE) except Exception as e: log.debug("Failed to sync license file: %s", e) return JsonResp(request, message=_('License updated.')) else: return JsonResp(request, form=form) else: form = forms.LicenseUpdateForm() return render(request, 'support/license_update.html', { 'form': form, 'license': license, })
def license_update(request): license, reason = utils.get_license() if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, 'wb+') as f: f.write(form.cleaned_data.get('license').encode('ascii')) events = [] try: _n = notifier() if not _n.is_freenas(): s = _n.failover_rpc() if s is not None: _n.sync_file_send(s, utils.LICENSE_FILE) form.done(request, events) except Exception as e: log.debug("Failed to sync license file: %s", e, exc_info=True) return JsonResp(request, events=events, message=_('License updated.')) else: return JsonResp(request, form=form) else: form = forms.LicenseUpdateForm() return render(request, 'support/license_update.html', { 'form': form, 'license': license, })
async def info(self): """ Returns basic system information. """ uptime = (await (await Popen( "env -u TZ uptime | awk -F', load averages:' '{ print $1 }'", stdout=subprocess.PIPE, shell=True, )).communicate())[0].decode().strip() serial = (await (await Popen( ['dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE, )).communicate())[0].decode().strip() or None product = (await (await Popen( ['dmidecode', '-s', 'system-product-name'], stdout=subprocess.PIPE, )).communicate())[0].decode().strip() or None license = get_license()[0] if license: license = { "contract_type": ContractType(license.contract_type).name.upper(), "contract_end": license.contract_end, } return { 'version': self.version(), 'hostname': socket.gethostname(), 'physmem': sysctl.filter('hw.physmem')[0].value, 'model': sysctl.filter('hw.model')[0].value, 'cores': sysctl.filter('hw.ncpu')[0].value, 'loadavg': os.getloadavg(), 'uptime': uptime, 'system_serial': serial, 'system_product': product, 'license': license, 'boottime': datetime.fromtimestamp( struct.unpack('l', sysctl.filter('kern.boottime')[0].value[:8])[0]), 'datetime': datetime.utcnow(), 'timezone': (await self.middleware.call('datastore.config', 'system.settings'))['stg_timezone'], }
def ticket(self, support, alerts): node = alert_node() dismisseds = [a.message_id for a in mAlert.objects.filter(node=node)] msgs = [] for alert in alerts: if alert.getId() not in dismisseds: msgs.append(str(alert)) if len(msgs) == 0: return serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE, encoding='utf8', ).communicate()[0].split('\n')[0].upper() license, reason = get_license() if license: company = license.customer_name.decode() else: company = 'Unknown' for name, verbose_name in ( ('name', 'Contact Name'), ('title', 'Contact Title'), ('email', 'Contact E-mail'), ('phone', 'Contact Phone'), ('secondary_name', 'Secondary Contact Name'), ('secondary_title', 'Secondary Contact Title'), ('secondary_email', 'Secondary Contact E-mail'), ('secondary_phone', 'Secondary Contact Phone'), ): value = getattr(support, name) if value: msgs += ['', '{}: {}'.format(verbose_name, value)] with client as c: try: rv = c.call('support.new_ticket', { 'title': 'Automatic alert (%s)' % serial, 'body': '\n'.join(msgs), 'version': get_sw_version().split('-', 1)[-1], 'debug': False, 'company': company, 'serial': serial, 'department': 20, 'category': 'Hardware', 'criticality': 'Loss of Functionality', 'environment': 'Production', 'name': 'Automatic Alert', 'email': '*****@*****.**', 'phone': '-', }, job=True) log.debug( f'Automatic alert ticket successfully created: {rv["url"]}' ) except ClientException as e: log.error(f'Failed to create a support ticket: {e.error}')
def license_status(request): sw_name = get_sw_name().lower() license, reason = utils.get_license() if (license is None and sw_name != 'freenas') or license.expired: return HttpResponse('PROMPT') return HttpResponse('OK')
def license_status(request): sw_name = get_sw_name().lower() license, reason = utils.get_license() if (license is None and sw_name != "freenas") or license.expired: return HttpResponse("PROMPT") return HttpResponse("OK")
def license_status(request): sw_name = get_sw_name().lower() license = utils.get_license()[0] if (license is None and sw_name != 'freenas') or (license is not None and license['expired']): return HttpResponse('PROMPT') return HttpResponse('OK')
def ticket(self, support, alerts): node = alert_node() dismisseds = [a.message_id for a in mAlert.objects.filter(node=node)] msgs = [] for alert in alerts: if alert.getId() not in dismisseds: msgs.append(str(alert)) if len(msgs) == 0: return serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE, encoding='utf8', ).communicate()[0].split('\n')[0].upper() license, reason = get_license() if license: company = license.customer_name.decode() else: company = 'Unknown' for name, verbose_name in ( ('name', 'Contact Name'), ('title', 'Contact Title'), ('email', 'Contact E-mail'), ('phone', 'Contact Phone'), ('secondary_name', 'Secondary Contact Name'), ('secondary_title', 'Secondary Contact Title'), ('secondary_email', 'Secondary Contact E-mail'), ('secondary_phone', 'Secondary Contact Phone'), ): value = getattr(support, name) if value: msgs += ['', '{}: {}'.format(verbose_name, value)] with client as c: try: rv = c.call('support.new_ticket', { 'title': 'Automatic alert (%s)' % serial, 'body': '\n'.join(msgs), 'version': get_sw_version().split('-', 1)[-1], 'debug': False, 'company': company, 'serial': serial, 'department': 20, 'category': 'Hardware', 'criticality': 'Loss of Functionality', 'environment': 'Production', 'name': 'Automatic Alert', 'email': '*****@*****.**', 'phone': '-', }, job=True) log.debug(f'Automatic alert ticket successfully created: {rv["url"]}') except ClientException as e: log.error(f'Failed to create a support ticket: {e.error}')
def ticket(self, alerts): node = alert_node() dismisseds = [ a.message_id for a in mAlert.objects.filter(dismiss=True, node=node) ] msgs = [] for alert in alerts: if alert.getId() not in dismisseds: msgs.append(unicode(alert).encode('utf8')) if len(msgs) == 0: return serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE).communicate()[0].split('\n')[0].upper() license, reason = get_license() if license: company = license.customer_name else: company = 'Unknown' success, msg, ticketnum = new_ticket({ 'title': 'Automatic alert (%s)' % serial, 'body': '\n'.join(msgs), 'version': get_sw_version().split('-', 1)[-1], 'debug': False, 'company': company, 'serial': serial, 'department': 20, 'category': 'Hardware', 'criticality': 'Loss of Functionality', 'environment': 'Production', 'name': 'Automatic Alert', 'email': '*****@*****.**', 'phone': '-', }) if not success: log.error("Failed to create a support ticket: %s", msg) else: log.debug("Automatic alert ticket successfully created: %s", msg)
def index(request): sw_name = get_sw_name().lower() license, reason = utils.get_license() allow_update = True if hasattr(notifier, "failover_status"): status = notifier().failover_status() if status not in ("MASTER", "SINGLE"): allow_update = False context = {"sw_name": sw_name, "license": license, "allow_update": allow_update} for c in appPool.hook_view_context("support.index", request): context.update(c) return render(request, "support/home.html", context)
def license_update(request): license, reason = utils.get_license() if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, 'wb+') as f: f.write(form.cleaned_data.get('license').encode('ascii')) with client as c: c.call('etc.generate', 'rc') events = [] try: _n = notifier() if not _n.is_freenas(): with client as c: _n.sync_file_send(c, utils.LICENSE_FILE) c.call('failover.call_remote', 'etc.generate', ['rc']) form.done(request, events) except Exception as e: log.debug("Failed to sync license file: %s", e, exc_info=True) return JsonResp(request, events=events, message=_('License updated.')) else: return JsonResp(request, form=form) else: _n = notifier() try: if not _n.is_freenas() and _n.failover_licensed(): with client as c: c.call('failover.call_remote', 'core.ping') except ClientException: return render(request, 'failover/failover_down.html') form = forms.LicenseUpdateForm() eula = None if not notifier().is_freenas(): if os.path.exists('/usr/local/share/truenas/eula.html'): with open('/usr/local/share/truenas/eula.html', 'r', encoding='utf8') as f: eula = f.read() return render(request, 'support/license_update.html', { 'eula': eula, 'form': form, 'license': license, })
def license_update(request): license, reason = utils.get_license() if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, 'wb+') as f: f.write(form.cleaned_data.get('license').encode('ascii')) return JsonResp(request, message=_('License updated.')) else: return JsonResp(request, form=form) else: form = forms.LicenseUpdateForm() return render(request, 'support/license_update.html', { 'form': form, 'license': license, })
def license_update(request): license, reason = utils.get_license() if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, 'wb+') as f: f.write(form.cleaned_data.get('license').encode('ascii')) events = [] try: _n = notifier() if not _n.is_freenas(): s = _n.failover_rpc() if s is not None: _n.sync_file_send(s, utils.LICENSE_FILE) form.done(request, events) except Exception as e: log.debug("Failed to sync license file: %s", e, exc_info=True) return JsonResp( request, events=events, message=_('License updated.') ) else: return JsonResp(request, form=form) else: _n = notifier() try: if not _n.is_freenas() and _n.failover_licensed(): s = _n.failover_rpc() s.ping() except socket.error: return render(request, 'failover/failover_down.html') form = forms.LicenseUpdateForm() eula = None if not notifier().is_freenas(): if os.path.exists('/usr/local/share/truenas/eula'): with open('/usr/local/share/truenas/eula', 'r') as f: eula = f.read().decode('utf8') return render(request, 'support/license_update.html', { 'eula': eula, 'form': form, 'license': license, })
def index(request): sw_name = get_sw_name().lower() license, reason = utils.get_license() allow_update = True if hasattr(notifier, 'failover_status'): status = notifier().failover_status() if status not in ('MASTER', 'SINGLE'): allow_update = False context = { 'sw_name': sw_name, 'license': license, 'allow_update': allow_update, } for c in appPool.hook_view_context('support.index', request): context.update(c) return render(request, 'support/home.html', context)
def index(request): if request.method == 'POST': if request.POST.get('eula') == 'unaccept': request.session.pop('noeula', None) with client as c: c.call('truenas.unaccept_eula') return HttpResponseRedirect('/') sw_name = get_sw_name().lower() license = utils.get_license()[0] allow_update = True if hasattr(notifier, 'failover_status'): status = notifier().failover_status() if status not in ('MASTER', 'SINGLE'): allow_update = False context = { 'sw_name': sw_name, 'license': license, 'fc_enabled': utils.fc_enabled(), 'allow_update': allow_update, } for c in appPool.hook_view_context('support.index', request): context.update(c) if not notifier().is_freenas(): with client as c: context['eula_not_accepted'] = not c.call('truenas.is_eula_accepted') if not notifier().is_freenas(): form = forms.ProductionForm() if request.method == 'POST': form = forms.ProductionForm(request.POST) if form.is_valid(): form.save() return JsonResp( request, message='Production status successfully updated.' ) context['production_form'] = form return render(request, 'support/home.html', context)
def ticket(self, alerts): node = alert_node() dismisseds = [a.message_id for a in mAlert.objects.filter(dismiss=True, node=node)] msgs = [] for alert in alerts: if alert.getId() not in dismisseds: msgs.append(unicode(alert).encode('utf8')) if len(msgs) == 0: return serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE ).communicate()[0].split('\n')[0].upper() license, reason = get_license() if license: company = license.customer_name else: company = 'Unknown' success, msg, ticketnum = new_ticket({ 'title': 'Automatic alert (%s)' % serial, 'body': '\n'.join(msgs), 'version': get_sw_version().split('-', 1)[-1], 'debug': False, 'company': company, 'serial': serial, 'department': 20, 'category': 'Hardware', 'criticality': 'Loss of Functionality', 'environment': 'Production', 'name': 'Automatic Alert', 'email': '*****@*****.**', 'phone': '-', }) if not success: log.error("Failed to create a support ticket: %s", msg) else: log.debug("Automatic alert ticket successfully created: %s", msg)
def license_update(request): license = utils.get_license()[0] if request.method == 'POST': form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with client as c: try: c.call('system.license_update', form.cleaned_data.get('license')) except Exception as e: form._errors['__all__'] = form.error_class([str(e)]) return JsonResp(request, form=form) return JsonResp( request, message=_('License updated.') ) else: return JsonResp(request, form=form) else: _n = notifier() try: if not _n.is_freenas() and _n.failover_licensed(): with client as c: c.call('failover.call_remote', 'core.ping') except ClientException: return render(request, 'failover/failover_down.html') form = forms.LicenseUpdateForm() eula = None if not notifier().is_freenas(): if os.path.exists('/usr/local/share/truenas/eula.html'): with open('/usr/local/share/truenas/eula.html', 'r', encoding='utf8') as f: eula = f.read() return render(request, 'support/license_update.html', { 'eula': eula, 'form': form, 'license': license, })
async def __get_license(self): licenseobj = get_license()[0] if not licenseobj: return license = { "system_serial": licenseobj.system_serial, "system_serial_ha": licenseobj.system_serial_ha, "contract_type": ContractType(licenseobj.contract_type).name.upper(), "contract_end": licenseobj.contract_end, "features": [], } for feature in licenseobj.features: license["features"].append(feature.name.upper()) # Licenses issued before 2017-04-14 had a bug in the feature bit # for fibre channel, which means they were issued having # dedup+jails instead. if (licenseobj.contract_start < date(2017, 4, 14) and Features.dedup in licenseobj.features and Features.jails in licenseobj.features): license["features"].append(Features.fibrechannel.name.upper()) return license
def license_update(request): license, reason = utils.get_license() if request.method == "POST": form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, "wb+") as f: f.write(form.cleaned_data.get("license").encode("ascii")) events = [] try: _n = notifier() if not _n.is_freenas(): s = _n.failover_rpc() if s is not None: _n.sync_file_send(s, utils.LICENSE_FILE) form.done(request, events) except Exception as e: log.debug("Failed to sync license file: %s", e, exc_info=True) return JsonResp(request, events=events, message=_("License updated.")) else: return JsonResp(request, form=form) else: _n = notifier() try: if not _n.is_freenas() and _n.failover_licensed(): s = _n.failover_rpc() s.ping() except socket.error: return render(request, "failover/failover_down.html") form = forms.LicenseUpdateForm() eula = None if not notifier().is_freenas(): if os.path.exists("/usr/local/share/truenas/eula"): with open("/usr/local/share/truenas/eula", "r") as f: eula = f.read().decode("utf8") return render(request, "support/license_update.html", {"eula": eula, "form": form, "license": license})
async def __get_license(self): licenseobj = get_license()[0] if not licenseobj: return license = { "system_serial": licenseobj.system_serial, "system_serial_ha": licenseobj.system_serial_ha, "contract_type": ContractType(licenseobj.contract_type).name.upper(), "contract_end": licenseobj.contract_end, "features": [], } for feature in licenseobj.features: license["features"].append(feature.name.upper()) # Licenses issued before 2017-04-14 had a bug in the feature bit # for fibre channel, which means they were issued having # dedup+jails instead. if ( licenseobj.contract_start < date(2017, 4, 14) and Features.dedup in licenseobj.features and Features.jails in licenseobj.features ): license["features"].append(Features.fibrechannel.name.upper()) return license
def license_update(request): license, reason = utils.get_license() if request.method == "POST": form = forms.LicenseUpdateForm(request.POST) if form.is_valid(): with open(utils.LICENSE_FILE, "wb+") as f: f.write(form.cleaned_data.get("license").encode("ascii")) events = [] try: _n = notifier() if not _n.is_freenas(): s = _n.failover_rpc() if s is not None: _n.sync_file_send(s, utils.LICENSE_FILE) form.done(request, events) except Exception as e: log.debug("Failed to sync license file: %s", e, exc_info=True) return JsonResp(request, events=events, message=_("License updated.")) else: return JsonResp(request, form=form) else: form = forms.LicenseUpdateForm() return render(request, "support/license_update.html", {"form": form, "license": license})
def hook_app_tabs_system(self, request): from freenasUI.freeadmin.sqlite3_ha.base import NO_SYNC_MAP from freenasUI.middleware.notifier import notifier from freenasUI.system import models from freenasUI.support.utils import get_license tabmodels = [ models.Settings, models.Advanced, models.Email, models.SystemDataset, models.Tunable, models.CloudCredentials, models.AlertDefaultSettings, models.AlertService, models.CertificateAuthority, models.Certificate, ] tabs = [] if ( hasattr(notifier, 'failover_status') and notifier().failover_status() == 'BACKUP' ): backup = True else: backup = False tabs.append({ 'name': 'SysInfo', 'focus': 'system.SysInfo', 'verbose_name': _('Information'), 'url': reverse('system_info'), }) for model in tabmodels: if backup and model._meta.db_table not in NO_SYNC_MAP: continue # System Dataset has only one hidden field if backup and model._meta.db_table == 'system_systemdataset': continue if model._admin.deletable is False: try: obj = model.objects.order_by('-id')[0] except IndexError: obj = model.objects.create() url = obj.get_edit_url() + '?inline=true' verbose_name = model._meta.verbose_name focus = 'system.%s' % model._meta.object_name else: url = reverse('freeadmin_%s_%s_datagrid' % ( model._meta.app_label, model._meta.model_name, )) verbose_name = model._meta.verbose_name_plural focus = 'system.%s.View' % model._meta.object_name tabs.append({ 'name': model._meta.object_name, 'focus': focus, 'verbose_name': verbose_name, 'url': url, }) tabs.insert(2, { 'name': 'BootEnv', 'focus': 'system.BootEnv', 'verbose_name': _('Boot'), 'url': reverse('system_bootenv_datagrid'), }) tabs.insert(8, { 'name': 'Update', 'focus': 'system.Update', 'verbose_name': _('Update'), 'url': reverse('system_update_index'), }) tabs.insert(13, { 'name': 'Support', 'focus': 'system.Support', 'verbose_name': _('Support'), 'url': reverse('support_home'), }) license = get_license()[0] if license is not None and not notifier().is_freenas(): support = models.Support.objects.order_by('-id')[0] tabs.insert(14, { 'name': 'Proactive Support', 'focus': 'system.ProactiveSupport', 'verbose_name': _('Proactive Support'), 'url': support.get_edit_url() + '?inline=true', }) tabs.insert(15, { 'name': 'ViewEnclosure', 'focus': 'storage.ViewEnclosure', 'verbose_name': _('View Enclosure'), 'url': reverse('storage_enclosure_status'), }) return tabs
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') # 20M filesize limit if os.path.getsize(debug_file) > 20971520: raise CallError('Debug too large to attach', errno.EFBIG) 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, }
def ticket(request): step = 2 if request.FILES.getlist("attachment") else 1 files = [] if request.POST.get("debug") == "on": debug = True with open(TICKET_PROGRESS, "w") as f: f.write(json.dumps({"indeterminate": True, "step": step})) step += 1 mntpt, direc, dump = debug_get_settings() debug_run(direc) files.append(File(open(dump, "rb"), name=os.path.basename(dump))) else: debug = False with open(TICKET_PROGRESS, "w") as f: f.write(json.dumps({"indeterminate": True, "step": step})) step += 1 data = { "title": request.POST.get("subject"), "body": request.POST.get("desc"), "version": get_sw_version().split("-", 1)[-1], "category": request.POST.get("category"), "debug": debug, } if get_sw_name().lower() == "freenas": data.update( { "user": request.POST.get("username"), "password": request.POST.get("password"), "type": request.POST.get("type"), } ) else: serial = ( subprocess.Popen(["/usr/local/sbin/dmidecode", "-s", "system-serial-number"], stdout=subprocess.PIPE) .communicate()[0] .split("\n")[0] .upper() ) license, reason = utils.get_license() if license: company = license.customer_name else: company = "Unknown" data.update( { "phone": request.POST.get("phone"), "name": request.POST.get("name"), "company": company, "email": request.POST.get("email"), "criticality": request.POST.get("criticality"), "environment": request.POST.get("environment"), "serial": serial, } ) success, msg, tid = utils.new_ticket(data) with open(TICKET_PROGRESS, "w") as f: f.write(json.dumps({"indeterminate": True, "step": step})) step += 1 data = {"message": msg, "error": not success} if not success: pass else: files.extend(request.FILES.getlist("attachment")) for f in files: success, attachmsg = utils.ticket_attach( {"user": request.POST.get("username"), "password": request.POST.get("password"), "ticketnum": tid}, f ) data = "<html><body><textarea>%s</textarea></boby></html>" % (json.dumps(data),) return HttpResponse(data)
def ticket(request): step = 2 if request.FILES.getlist('attachment') else 1 files = [] if request.POST.get('debug') == 'on': debug = True with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 mntpt, direc, dump = debug_get_settings() debug_generate() _n = notifier() if not _n.is_freenas() and _n.failover_licensed(): debug_file = '%s/debug.tar' % direc debug_name = 'debug-%s.tar' % time.strftime('%Y%m%d%H%M%S') else: gc = GlobalConfiguration.objects.all().order_by('-id')[0] debug_file = dump debug_name = 'debug-%s-%s.txz' % ( gc.gc_hostname.encode('utf-8'), time.strftime('%Y%m%d%H%M%S'), ) files.append(File(open(debug_file, 'rb'), name=debug_name)) else: debug = False with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = { 'title': request.POST.get('subject'), 'body': request.POST.get('desc'), 'version': get_sw_version().split('-', 1)[-1], 'category': request.POST.get('category'), 'debug': debug, } if get_sw_name().lower() == 'freenas': data.update({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'type': request.POST.get('type'), }) else: serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE ).communicate()[0].split('\n')[0].upper() license, reason = utils.get_license() if license: company = license.customer_name else: company = 'Unknown' data.update({ 'phone': request.POST.get('phone'), 'name': request.POST.get('name'), 'company': company, 'email': request.POST.get('email'), 'criticality': request.POST.get('criticality'), 'environment': request.POST.get('environment'), 'serial': serial, }) success, msg, tid = utils.new_ticket(data) with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = {'message': msg, 'error': not success} if not success: pass else: files.extend(request.FILES.getlist('attachment')) for f in files: success, attachmsg = utils.ticket_attach({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'ticketnum': tid, }, f) data = ( '<html><body><textarea>%s</textarea></boby></html>' % ( json.dumps(data), ) ) return HttpResponse(data)
def ticket(request): step = 2 if request.FILES.getlist('attachment') else 1 files = [] if request.POST.get('debug') == 'on': debug = True with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 mntpt, direc, dump = debug_get_settings() debug_run(direc) files.append(File(open(dump, 'rb'), name=os.path.basename(dump))) else: debug = False with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = { 'title': request.POST.get('subject'), 'body': request.POST.get('desc'), 'version': get_sw_version().split('-', 1)[-1], 'category': request.POST.get('category'), 'debug': debug, } if get_sw_name().lower() == 'freenas': data.update({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'type': request.POST.get('type'), }) else: serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE ).communicate()[0].split('\n')[0].upper() license, reason = utils.get_license() if license: company = license.customer_name else: company = 'Unknown' data.update({ 'phone': request.POST.get('phone'), 'name': request.POST.get('name'), 'company': company, 'email': request.POST.get('email'), 'criticality': request.POST.get('criticality'), 'environment': request.POST.get('environment'), 'serial': serial, }) success, msg, tid = utils.new_ticket(data) with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = {'message': msg, 'error': not success} if not success: pass else: files.extend(request.FILES.getlist('attachment')) for f in files: success, attachmsg = utils.ticket_attach({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'ticketnum': tid, }, f) data = ( '<html><body><textarea>%s</textarea></boby></html>' % ( json.dumps(data), ) ) return HttpResponse(data)
async def process_alerts(self, job): if not await self.middleware.call("system.ready"): return if (not await self.middleware.call('system.is_freenas') and await self.middleware.call('notifier.failover_licensed') and await self.middleware.call('notifier.failover_status') == 'BACKUP'): return await self.__run_alerts() default_settings = ( await self.middleware.call("alertdefaultsettings.config"))["settings"] all_alerts = self.__get_all_alerts() now = datetime.now() for policy_name, policy in self.policies.items(): gone_alerts, new_alerts = policy.receive_alerts(now, self.alerts) for alert_service_desc in await self.middleware.call( "datastore.query", "system.alertservice"): service_settings = dict(default_settings, **alert_service_desc["settings"]) service_gone_alerts = [ alert for alert in gone_alerts if service_settings.get( alert.source, DEFAULT_POLICY) == policy_name ] service_new_alerts = [ alert for alert in new_alerts if service_settings.get( alert.source, DEFAULT_POLICY) == policy_name ] if not service_gone_alerts and not service_new_alerts: continue factory = ALERT_SERVICES_FACTORIES.get( alert_service_desc["type"]) if factory is None: self.logger.error("Alert service %r does not exist", alert_service_desc["type"]) continue try: alert_service = factory(self.middleware, alert_service_desc["attributes"]) except Exception: self.logger.error( "Error creating alert service %r with parameters=%r", alert_service_desc["type"], alert_service_desc["attributes"], exc_info=True) continue if all_alerts or service_gone_alerts or service_new_alerts: try: await alert_service.send(all_alerts, service_gone_alerts, service_new_alerts) except Exception: self.logger.error("Error in alert service %r", alert_service_desc["type"], exc_info=True) if policy_name == "IMMEDIATELY": for alert in new_alerts: if alert.mail: await self.middleware.call("mail.send", alert.mail) if not await self.middleware.call("system.is_freenas"): new_hardware_alerts = [ alert for alert in new_alerts if ALERT_SOURCES[alert.source].hardware ] if new_hardware_alerts: license = get_license() if license and license.contract_type in [ ContractType.silver.value, ContractType.gold.value ]: try: support = await self.middleware.call( "datastore.query", "system.support", None, {"get": True}) except IndexError: await self.middleware.call( "datastore.insert", "system.support", {}) support = await self.middleware.call( "datastore.query", "system.support", None, {"get": True}) if support["enabled"]: msg = [ f"* {alert.formatted}" for alert in new_hardware_alerts ] serial = (await self.middleware.call("system.info") )["system_serial"] for name, verbose_name in ( ("name", "Contact Name"), ("title", "Contact Title"), ("email", "Contact E-mail"), ("phone", "Contact Phone"), ("secondary_name", "Secondary Contact Name"), ("secondary_title", "Secondary Contact Title"), ("secondary_email", "Secondary Contact E-mail"), ("secondary_phone", "Secondary Contact Phone"), ): value = getattr(support, name) if value: msg += [ "", "{}: {}".format( verbose_name, value) ] try: await self.middleware.call( "support.new_ticket", { "title": "Automatic alert (%s)" % serial, "body": "\n".join(msg), "attach_debug": False, "category": "Hardware", "criticality": "Loss of Functionality", "environment": "Production", "name": "Automatic Alert", "email": "*****@*****.**", "phone": "-", }) except Exception: self.logger.error( f"Failed to create a support ticket", exc_info=True)
def hook_app_tabs_system(self, request): from freenasUI.freeadmin.sqlite3_ha.base import NO_SYNC_MAP from freenasUI.middleware.notifier import notifier from freenasUI.system import models from freenasUI.support.utils import get_license tabmodels = [ models.Settings, models.Advanced, models.Email, models.SystemDataset, models.Tunable, models.CloudCredentials, models.AlertDefaultSettings, models.AlertService, models.CertificateAuthority, models.Certificate, models.ACMEDNSAuthenticator ] tabs = [] if (hasattr(notifier, 'failover_status') and notifier().failover_status() == 'BACKUP'): backup = True else: backup = False tabs.append({ 'name': 'SysInfo', 'focus': 'system.SysInfo', 'verbose_name': _('Information'), 'url': reverse('system_info'), }) for model in tabmodels: if backup and model._meta.db_table not in NO_SYNC_MAP: continue # System Dataset has only one hidden field if backup and model._meta.db_table == 'system_systemdataset': continue if model._admin.deletable is False: try: obj = model.objects.order_by('-id')[0] except IndexError: obj = model.objects.create() url = obj.get_edit_url() + '?inline=true' verbose_name = model._meta.verbose_name focus = 'system.%s' % model._meta.object_name else: url = reverse('freeadmin_%s_%s_datagrid' % ( model._meta.app_label, model._meta.model_name, )) verbose_name = model._meta.verbose_name_plural focus = 'system.%s.View' % model._meta.object_name tabs.append({ 'name': model._meta.object_name, 'focus': focus, 'verbose_name': verbose_name, 'url': url, }) tabs.insert( 2, { 'name': 'BootEnv', 'focus': 'system.BootEnv', 'verbose_name': _('Boot'), 'url': reverse('system_bootenv_datagrid'), }) tabs.insert( 8, { 'name': 'Update', 'focus': 'system.Update', 'verbose_name': _('Update'), 'url': reverse('system_update_index'), }) tabs.insert( 13, { 'name': 'Support', 'focus': 'system.Support', 'verbose_name': _('Support'), 'url': reverse('support_home'), 'onload': 'support_production_init();' if not notifier().is_freenas() else '' }) license = get_license()[0] if license is not None and not notifier().is_freenas(): support = models.Support.objects.order_by('-id')[0] tabs.insert( 14, { 'name': 'Proactive Support', 'focus': 'system.ProactiveSupport', 'verbose_name': _('Proactive Support'), 'url': support.get_edit_url() + '?inline=true', }) tabs.insert( 15, { 'name': 'ViewEnclosure', 'focus': 'system.ViewEnclosure', 'verbose_name': _('View Enclosure'), 'url': reverse('storage_enclosure_status'), }) return tabs
def ticket(request): step = 2 if request.FILES.getlist('attachment') else 1 files = [] if request.POST.get('debug') == 'on': debug = True with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 mntpt, direc, dump = debug_get_settings() debug_generate() _n = notifier() if not _n.is_freenas() and _n.failover_licensed(): debug_file = '%s/debug.tar' % direc debug_name = 'debug-%s.tar' % time.strftime('%Y%m%d%H%M%S') else: gc = GlobalConfiguration.objects.all().order_by('-id')[0] debug_file = dump debug_name = 'debug-%s-%s.txz' % ( gc.gc_hostname.encode('utf-8'), time.strftime('%Y%m%d%H%M%S'), ) files.append(File(open(debug_file, 'rb'), name=debug_name)) else: debug = False with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = { 'title': request.POST.get('subject'), 'body': request.POST.get('desc'), 'version': get_sw_version().split('-', 1)[-1], 'category': request.POST.get('category'), 'debug': debug, } if get_sw_name().lower() == 'freenas': data.update({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'type': request.POST.get('type'), }) else: serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE).communicate()[0].split('\n')[0].upper() license, reason = utils.get_license() if license: company = license.customer_name else: company = 'Unknown' data.update({ 'phone': request.POST.get('phone'), 'name': request.POST.get('name'), 'company': company, 'email': request.POST.get('email'), 'criticality': request.POST.get('criticality'), 'environment': request.POST.get('environment'), 'serial': serial, }) success, msg, tid = utils.new_ticket(data) with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = {'message': msg, 'error': not success} if not success: pass else: files.extend(request.FILES.getlist('attachment')) for f in files: success, attachmsg = utils.ticket_attach( { 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'ticketnum': tid, }, f) data = ('<html><body><textarea>%s</textarea></boby></html>' % (json.dumps(data), )) return HttpResponse(data)
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 process_alerts(self, job): if not await self.middleware.call("system.ready"): return if ( not await self.middleware.call('system.is_freenas') and await self.middleware.call('notifier.failover_licensed') and await self.middleware.call('notifier.failover_status') == 'BACKUP' ): return await self.__run_alerts() default_settings = (await self.middleware.call("alertdefaultsettings.config"))["settings"] all_alerts = self.__get_all_alerts() now = datetime.now() for policy_name, policy in self.policies.items(): gone_alerts, new_alerts = policy.receive_alerts(now, self.alerts) for alert_service_desc in await self.middleware.call("datastore.query", "system.alertservice"): service_settings = dict(default_settings, **alert_service_desc["settings"]) service_gone_alerts = [alert for alert in gone_alerts if service_settings.get(alert.source, DEFAULT_POLICY) == policy_name] service_new_alerts = [alert for alert in new_alerts if service_settings.get(alert.source, DEFAULT_POLICY) == policy_name] if not service_gone_alerts and not service_new_alerts: continue factory = ALERT_SERVICES_FACTORIES.get(alert_service_desc["type"]) if factory is None: self.logger.error("Alert service %r does not exist", alert_service_desc["type"]) continue try: alert_service = factory(self.middleware, alert_service_desc["attributes"]) except Exception: self.logger.error("Error creating alert service %r with parameters=%r", alert_service_desc["type"], alert_service_desc["attributes"], exc_info=True) continue if all_alerts or service_gone_alerts or service_new_alerts: try: await alert_service.send(all_alerts, service_gone_alerts, service_new_alerts) except Exception: self.logger.error("Error in alert service %r", alert_service_desc["type"], exc_info=True) if policy_name == "IMMEDIATELY": for alert in new_alerts: if alert.mail: await self.middleware.call("mail.send", alert.mail) if not await self.middleware.call("system.is_freenas"): new_hardware_alerts = [alert for alert in new_alerts if ALERT_SOURCES[alert.source].hardware] if new_hardware_alerts: license = get_license() if license and license.contract_type in [ContractType.silver.value, ContractType.gold.value]: try: support = await self.middleware.call("datastore.query", "system.support", None, {"get": True}) except IndexError: await self.middleware.call("datastore.insert", "system.support", {}) support = await self.middleware.call("datastore.query", "system.support", None, {"get": True}) if support["enabled"]: msg = [f"* {alert.formatted}" for alert in new_hardware_alerts] serial = await self.middleware.call("system._serial") for name, verbose_name in ( ("name", "Contact Name"), ("title", "Contact Title"), ("email", "Contact E-mail"), ("phone", "Contact Phone"), ("secondary_name", "Secondary Contact Name"), ("secondary_title", "Secondary Contact Title"), ("secondary_email", "Secondary Contact E-mail"), ("secondary_phone", "Secondary Contact Phone"), ): value = getattr(support, name) if value: msg += ["", "{}: {}".format(verbose_name, value)] try: await self.middleware.call("support.new_ticket", { "title": "Automatic alert (%s)" % serial, "body": "\n".join(msg), "attach_debug": False, "category": "Hardware", "criticality": "Loss of Functionality", "environment": "Production", "name": "Automatic Alert", "email": "*****@*****.**", "phone": "-", }) except Exception: self.logger.error(f"Failed to create a support ticket", exc_info=True)
def ticket(request): step = 2 if request.FILES.getlist('attachment') else 1 files = [] if request.POST.get('debug') == 'on': debug = True with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 mntpt, direc, dump = debug_get_settings() debug_run(direc) files.append(File(open(dump, 'rb'), name=os.path.basename(dump))) else: debug = False with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = { 'title': request.POST.get('subject'), 'body': request.POST.get('desc'), 'version': get_sw_version().split('-', 1)[-1], 'category': request.POST.get('category'), 'debug': debug, } if get_sw_name().lower() == 'freenas': data.update({ 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'type': request.POST.get('type'), }) else: serial = subprocess.Popen( ['/usr/local/sbin/dmidecode', '-s', 'system-serial-number'], stdout=subprocess.PIPE).communicate()[0].split('\n')[0].upper() license, reason = utils.get_license() if license: company = license.customer_name else: company = 'Unknown' data.update({ 'phone': request.POST.get('phone'), 'name': request.POST.get('name'), 'company': company, 'email': request.POST.get('email'), 'criticality': request.POST.get('criticality'), 'environment': request.POST.get('environment'), 'serial': serial, }) success, msg, tid = utils.new_ticket(data) with open(TICKET_PROGRESS, 'w') as f: f.write(json.dumps({'indeterminate': True, 'step': step})) step += 1 data = {'message': msg, 'error': not success} if not success: pass else: files.extend(request.FILES.getlist('attachment')) for f in files: success, attachmsg = utils.ticket_attach( { 'user': request.POST.get('username'), 'password': request.POST.get('password'), 'ticketnum': tid, }, f) data = ('<html><body><textarea>%s</textarea></boby></html>' % (json.dumps(data), )) return HttpResponse(data)