class idmap_hash(idmap_base): idmap_hash_range_low = models.IntegerField( verbose_name=_("Range Low"), default=90000001 ) idmap_hash_range_high = models.IntegerField( verbose_name=_("Range High"), default=100000000 ) idmap_hash_range_name_map = PathField( verbose_name=_("Name Map"), help_text=_( 'Specifies the absolute path to the name mapping file ' 'used by the nss_info API. Entries in the file are of ' 'the form "unix name = qualified domain name". Mapping ' 'of both user and group names is supported.' ) ) def __init__(self, *args, **kwargs): super(idmap_hash, self).__init__(*args, **kwargs) self.idmap_backend_type = IDMAP_TYPE_HASH self.idmap_backend_name = enum_to_idmap(self.idmap_backend_type) class Meta: verbose_name = _("Hash Idmap") verbose_name_plural = _("Hash Idmap") class FreeAdmin: resource_name = 'directoryservice/idmap/hash'
class idmap_tdb2(idmap_base): idmap_tdb2_range_low = models.IntegerField( verbose_name=_("Range Low"), default=90000001 ) idmap_tdb2_range_high = models.IntegerField( verbose_name=_("Range High"), default=100000000 ) idmap_tdb2_script = PathField( verbose_name=_("Script"), help_text=_( "This option can be used to configure an external program for " "performing id mappings instead of using the tdb counter. The " "mappings are then stored in the tdb2 idmap database." ) ) def __init__(self, *args, **kwargs): super(idmap_tdb2, self).__init__(*args, **kwargs) self.idmap_backend_type = IDMAP_TYPE_TDB2 self.idmap_backend_name = enum_to_idmap(self.idmap_backend_type) class Meta: verbose_name = _("TDB2 Idmap") verbose_name_plural = _("TDB2 Idmap") class FreeAdmin: resource_name = 'directoryservice/idmap/tdb2'
class idmap_script(Model): idmap_script_domain = models.OneToOneField( Idmap_Domain, on_delete=models.deletion.CASCADE, to_field='idmap_domain_name', unique=True, null=True, verbose_name=_('pre-Windows 2000 Domain Name'), ) idmap_script_range_low = models.IntegerField( verbose_name=_("Range Low"), default=90000001 ) idmap_script_range_high = models.IntegerField( verbose_name=_("Range High"), default=100000000 ) idmap_script_script = PathField( verbose_name=_("Script"), help_text=_( "This option is used to configure an external program for " "performing id mappings. This is read-only backend and relies on " "winbind_cache tdb to store obtained values" ) ) def __init__(self, *args, **kwargs): super(idmap_script, self).__init__(*args, **kwargs) class Meta: verbose_name = _("Script Idmap") verbose_name_plural = _("Script Idmap") class FreeAdmin: resource_name = 'directoryservice/idmap/script'
class idmap_script(idmap_base): idmap_script_range_low = models.IntegerField( verbose_name=_("Range Low"), default=90000001 ) idmap_script_range_high = models.IntegerField( verbose_name=_("Range High"), default=100000000 ) idmap_script_script = PathField( verbose_name=_("Script"), help_text=_( "This option is used to configure an external program for " "performing id mappings. This is read-only backend and relies on " "winbind_cache tdb to store obtained values" ) ) def __init__(self, *args, **kwargs): super(idmap_script, self).__init__(*args, **kwargs) self.idmap_backend_type = IDMAP_TYPE_SCRIPT self.idmap_backend_name = enum_to_idmap(self.idmap_backend_type) class Meta: verbose_name = _("Script Idmap") verbose_name_plural = _("Script Idmap") class FreeAdmin: resource_name = 'directoryservice/idmap/script'
class CIFS_Share(Model): cifs_name = models.CharField(max_length=120, verbose_name=_("Name")) cifs_comment = models.CharField( max_length=120, verbose_name=_("Comment"), blank=True, ) cifs_path = PathField(verbose_name=_("Path")) cifs_ro = models.BooleanField(verbose_name=_("Export Read Only")) cifs_browsable = models.BooleanField( verbose_name=_("Browsable to Network Clients"), default=True) cifs_inheritowner = models.BooleanField(verbose_name=_("Inherit Owner"), default=False) cifs_inheritperms = models.BooleanField( verbose_name=_("Inherit Permissions")) cifs_recyclebin = models.BooleanField(verbose_name=_("Export Recycle Bin")) cifs_showhiddenfiles = models.BooleanField( verbose_name=_("Show Hidden Files")) cifs_guestok = models.BooleanField( verbose_name=_("Allow Guest Access"), help_text=_( "If true then no password is required to connect to " "the share. Privileges will be those of the guest account.")) cifs_guestonly = models.BooleanField( verbose_name=_("Only Allow Guest Access"), help_text=_( "If true then only guest connections to the share " "are permitted. This parameter will have no effect if Allow " "Guest Access is not set for the share.")) cifs_hostsallow = models.TextField( blank=True, verbose_name=_("Hosts Allow"), help_text= _("This option is a comma, space, or tab delimited set of hosts which are permitted to access this share. You can specify the hosts by name or IP number. Leave this field empty to use default settings." )) cifs_hostsdeny = models.TextField( blank=True, verbose_name=_("Hosts Deny"), help_text= _("This option is a comma, space, or tab delimited set of host which are NOT permitted to access this share. Where the lists conflict, the allow list takes precedence. In the event that it is necessary to deny all by default, use the keyword ALL (or the netmask 0.0.0.0/0) and then explicitly specify to the hosts allow parameter those hosts that should be permitted access. Leave this field empty to use default settings." )) cifs_auxsmbconf = models.TextField( max_length=120, verbose_name=_("Auxiliary Parameters"), blank=True, help_text=_( "These parameters are added to [Share] section of smb.conf")) def __unicode__(self): return self.cifs_name def delete(self, *args, **kwargs): super(CIFS_Share, self).delete(*args, **kwargs) notifier().reload("cifs") class Meta: verbose_name = _("Windows (CIFS) Share") verbose_name_plural = _("Windows (CIFS) Shares") ordering = ["cifs_name"]
class InitShutdown(Model): ini_type = models.CharField( choices=( ('command', _('Command')), ('script', _('Script')), ), default='command', max_length=15, verbose_name=_("Type"), ) ini_command = models.CharField( max_length=300, verbose_name=_("Command"), blank=True, ) ini_script = PathField( verbose_name=_("Script"), filesonly=True, dirsonly=False, blank=True, ) ini_when = models.CharField( choices=( ('preinit', _('Pre Init')), ('postinit', _('Post Init')), ('shutdown', _('Shutdown')), ), max_length=15, verbose_name=_("When"), ) ini_enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), ) def __str__(self): if self.ini_type == 'command': name = self.ini_command else: name = self.ini_script return "%s - %s" % ( self.get_ini_when_display(), name, ) class Meta: verbose_name = _("Init/Shutdown Script") verbose_name_plural = _("Init/Shutdown Scripts") class FreeAdmin: # FIXME icon_model = "TunableIcon" icon_object = "TunableIcon" icon_add = "AddTunableIcon" icon_view = "ViewTunableIcon" menu_child_of = 'tasks'
class NFS_Share_Path(Model): share = models.ForeignKey(NFS_Share, related_name="paths") path = PathField(verbose_name=_("Path")) def __unicode__(self): return self.path class Meta: verbose_name = _("Path") verbose_name_plural = _("Paths")
class InitShutdown(Model): ini_type = models.CharField( choices=( ('command', _('Command')), ('script', _('Script')), ), default='command', max_length=15, verbose_name=_("Type"), ) ini_command = models.CharField( max_length=300, verbose_name=_("Command"), blank=True, ) ini_script = PathField( verbose_name=_("Script"), blank=True, ) ini_when = models.CharField( choices=( ('preinit', _('Pre Init')), ('postinit', _('Post Init')), ('shutdown', _('Shutdown')), ), max_length=15, verbose_name=_("Type"), ) def __unicode__(self): if self.ini_type == 'command': name = self.ini_command else: name = self.ini_script return u"%s - %s" % ( self.get_ini_when_display(), name, ) class Meta: verbose_name = _("Init/Shutdown Script") verbose_name_plural = _("Init/Shutdown Scripts") class FreeAdmin: #FIXME icon_model = u"TunableIcon" icon_object = u"TunableIcon" icon_add = u"AddTunableIcon" icon_view = u"ViewTunableIcon"
class WebDAV_Share(Model): webdav_name = models.CharField( max_length=120, verbose_name=_("Share Name"), help_text=_( "This will be used to access your WebDAV share." "<br />For example http(s)://ip-of-freenas-machine:webdav_port/'Share Name'" ), ) webdav_comment = models.CharField( max_length=120, verbose_name=_("Comment"), blank=True, ) webdav_path = PathField(verbose_name=_("Path")) webdav_ro = models.BooleanField( verbose_name=_('Read Only'), help_text=_('Export the share read only. Writes are not permitted.'), default=False, ) webdav_perm = models.BooleanField( verbose_name=_('Change User & Group Ownership'), help_text=_( "Changes the user & group of the shared folder" " to 'webdav:webdav' recursively (including all subdirectories)" "<br />If disabled, you will need to manually" "<br />add the 'webdav' user & group to the share."), default=True, ) def __unicode__(self): return unicode(self.webdav_name) def delete(self, *args, **kwargs): super(WebDAV_Share, self).delete(*args, **kwargs) notifier().reload("webdav") class Meta: verbose_name = _("WebDAV Share") verbose_name_plural = _("WebDAV Shares") ordering = ["webdav_name"]
class CloudSync(Model): description = models.CharField( max_length=150, verbose_name=_('Description'), ) path = PathField( verbose_name=_("Path"), abspath=False, ) credential = models.ForeignKey( 'system.CloudCredentials', verbose_name=_('Credential'), ) attributes = DictField(editable=False, ) minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_("Values allowed:" "<br>Slider: 0-30 (as it is every Nth minute)." "<br>Specific Minute: 0-59."), ) hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values allowed:" "<br>Slider: 0-12 (as it is every Nth hour)." "<br>Specific Hour: 0-23."), ) daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values allowed:" "<br>Slider: 0-15 (as its is every Nth day)." "<br>Specific Day: 1-31."), ) month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), help_text=_( 'Disabling will not stop any syncs which are in progress.'), ) class Meta: verbose_name = _(u"Cloud Sync") verbose_name_plural = _(u"Cloud Syncs") ordering = ["description"] def __unicode__(self): return self.description def get_human_minute(self): if self.minute == '*': return _(u'Every minute') elif self.minute.startswith('*/'): return _(u'Every {0} minute(s)').format(self.minute.split('*/')[1]) else: return self.minute def get_human_hour(self): if self.hour == '*': return _(u'Every hour') elif self.hour.startswith('*/'): return _(u'Every {0} hour(s)').format(self.hour.split('*/')[1]) else: return self.hour def get_human_daymonth(self): if self.daymonth == '*': return _(u'Everyday') elif self.daymonth.startswith('*/'): return _(u'Every {0} days').format(self.daymonth.split('*/')[1]) else: return self.daymonth def get_human_month(self): months = self.month.split(',') if len(months) == 12 or self.month == '*': return _("Every month") mchoices = dict(choices.MONTHS_CHOICES) labels = [] for m in months: labels.append(unicode(mchoices[m])) return ', '.join(labels) def get_human_dayweek(self): weeks = self.dayweek.split(',') if len(weeks) == 7 or self.dayweek == '*': return _('Everyday') if weeks == map(str, xrange(1, 6)): return _('Weekdays') if weeks == map(str, xrange(6, 8)): return _('Weekends') wchoices = dict(choices.WEEKDAYS_CHOICES) labels = [] for w in weeks: labels.append(unicode(wchoices[str(w)])) return ', '.join(labels) def run(self): with client as c: jid = c.call('backup.sync', self.id) return jid
class Rsync(Model): rsync_path = PathField( verbose_name=_("Path"), abspath=False, ) rsync_remotehost = models.CharField( max_length=120, verbose_name=_("Remote Host"), help_text=_("IP Address or hostname. " "Specify user@hostname or user@ip-address " "if your remote machine user and above rsync " "task user are different."), ) rsync_remoteport = models.SmallIntegerField( default=22, verbose_name=_("Remote SSH Port"), help_text=_("SSH Port"), validators=[MinValueValidator(1), MaxValueValidator(65535)], ) rsync_mode = models.CharField( max_length=20, choices=choices.RSYNC_MODE_CHOICES, default='module', ) rsync_remotemodule = models.CharField( max_length=120, verbose_name=_("Remote Module Name"), blank=True, help_text=_("Name of the module defined in the remote rsync daemon"), ) rsync_remotepath = models.CharField( max_length=255, verbose_name=_("Remote Path"), blank=True, help_text=_("Path on remote host to rsync to, e.g. /mnt/tank"), ) rsync_direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_("Push - From local to remote machine. Pull - From " "remote to local machine."), default='push', choices=choices.RSYNC_DIRECTION, ) rsync_desc = models.CharField( max_length=120, verbose_name=_("Short description"), blank=True, ) rsync_minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_("Values allowed:" "<br>Slider: 0-30 (as it is every Nth minute)." "<br>Specific Minute: 0-59."), ) rsync_hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values allowed:" "<br>Slider: 0-12 (as it is every Nth hour)." "<br>Specific Hour: 0-23."), ) rsync_daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values allowed:" "<br>Slider: 0-15 (as its is every Nth day)." "<br>Specific Day: 1-31."), ) rsync_month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) rsync_dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) rsync_user = UserField( max_length=60, verbose_name=_("User"), help_text=_("The user to run the command"), ) rsync_recursive = models.BooleanField( verbose_name=_("Recursive"), help_text=_("Recurse into directories"), default=True, ) rsync_times = models.BooleanField( verbose_name=_("Times"), help_text=_("Preserve modification times"), default=True, ) rsync_compress = models.BooleanField( verbose_name=_("Compress"), help_text=_("Compress data during the transfer"), default=True, ) rsync_archive = models.BooleanField( verbose_name=_("Archive"), help_text=_("Archive mode"), default=False, ) rsync_delete = models.BooleanField( verbose_name=_("Delete"), help_text=_( "Delete files on the receiving side that don't exist on sender"), default=False, ) rsync_quiet = models.BooleanField( verbose_name=_("Quiet"), help_text=_("Suppress non-error messages"), default=False, ) rsync_preserveperm = models.BooleanField( verbose_name=_("Preserve permissions"), help_text=_("This option causes the receiving rsync to set the " "destination permissions to be the same as the source " "permissions"), default=False, ) rsync_preserveattr = models.BooleanField( verbose_name=_("Preserve extended attributes"), help_text=_("This option causes rsync to update the remote " "extended attributes to be the same as the local ones"), default=False, ) rsync_delayupdates = models.BooleanField( verbose_name=_("Delay Updates"), help_text=_("Put all updated files into place at the end"), default=True, ) rsync_extra = models.TextField( verbose_name=_("Extra options"), help_text=_("Extra options to rsync command line (usually empty)"), blank=True) rsync_enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), ) class Meta: verbose_name = _("Rsync Task") verbose_name_plural = _("Rsync Tasks") ordering = ["rsync_path", "rsync_desc"] def __unicode__(self): if self.rsync_desc: return self.rsync_desc elif self.rsync_mode == 'module': return self.rsync_remotemodule else: return self.rsync_remotepath def get_human_minute(self): if self.rsync_minute == '*': return _(u'Every minute') elif self.rsync_minute.startswith('*/'): return _(u'Every {0} minute(s)').format( self.rsync_minute.split('*/')[1]) else: return self.rsync_minute def get_human_hour(self): if self.rsync_hour == '*': return _(u'Every hour') elif self.rsync_hour.startswith('*/'): return _(u'Every {0} hour(s)').format( self.rsync_hour.split('*/')[1]) else: return self.rsync_hour def get_human_daymonth(self): if self.rsync_daymonth == '*': return _(u'Everyday') elif self.rsync_daymonth.startswith('*/'): return _(u'Every {0} days').format( self.rsync_daymonth.split('*/')[1]) else: return self.rsync_daymonth def get_human_month(self): months = self.rsync_month.split(',') if len(months) == 12 or self.rsync_month == '*': return _("Every month") mchoices = dict(choices.MONTHS_CHOICES) labels = [] for m in months: labels.append(unicode(mchoices[m])) return ', '.join(labels) def get_human_dayweek(self): weeks = self.rsync_dayweek.split(',') if len(weeks) == 7 or self.rsync_dayweek == '*': return _('Everyday') if weeks == map(str, xrange(1, 6)): return _('Weekdays') if weeks == map(str, xrange(6, 8)): return _('Weekends') wchoices = dict(choices.WEEKDAYS_CHOICES) labels = [] for w in weeks: labels.append(unicode(wchoices[str(w)])) return ', '.join(labels) def commandline(self): line = '/usr/bin/lockf -s -t 0 -k "%s" /usr/local/bin/rsync' % ( self.rsync_path) if self.rsync_recursive: line += ' -r' if self.rsync_times: line += ' -t' if self.rsync_compress: line += ' -z' if self.rsync_archive: line += ' -a' if self.rsync_preserveperm: line += ' -p' if self.rsync_preserveattr: line += ' -X' if self.rsync_delete: line += ' --delete-delay' if self.rsync_delayupdates: line += ' --delay-updates' if self.rsync_extra: line += ' %s' % self.rsync_extra # Do not use username if one is specified in host field # See #5096 for more details if '@' in self.rsync_remotehost: remote = self.rsync_remotehost else: remote = '"%s"@%s' % ( self.rsync_user, self.rsync_remotehost, ) if self.rsync_mode == 'module': if self.rsync_direction == 'push': line += ' "%s" %s::"%s"' % ( self.rsync_path, remote, self.rsync_remotemodule, ) else: line += ' %s::"%s" "%s"' % ( remote, self.rsync_remotemodule, self.rsync_path, ) else: line += (' -e "ssh -p %d -o BatchMode=yes ' '-o StrictHostKeyChecking=yes"') % (self.rsync_remoteport) if self.rsync_direction == 'push': line += ' "%s" %s:\\""%s"\\"' % ( self.rsync_path, remote, self.rsync_remotepath, ) else: line += ' %s:\\""%s"\\" "%s"' % ( remote, self.rsync_remotepath, self.rsync_path, ) if self.rsync_quiet: line += ' > /dev/null 2>&1' return line def run(self): proc = subprocess.Popen([ "/usr/local/bin/python2", "/usr/local/www/freenasUI/tools/runnow.py", "-t", "rsync", "-i", str(self.id), ]) proc.communicate() def delete(self): super(Rsync, self).delete() try: notifier().restart("cron") except: pass
class bsdUsers(Model): USERNAME_FIELD = 'bsdusr_username' REQUIRED_FIELDS = [] bsdusr_uid = models.IntegerField( verbose_name=_("User ID") ) bsdusr_username = models.CharField( max_length=16, unique=True, default=_('User &'), verbose_name=_("Username") ) bsdusr_unixhash = models.CharField( max_length=128, blank=True, default='*', verbose_name=_("Hashed UNIX password") ) bsdusr_smbhash = models.CharField( max_length=128, blank=True, default='*', verbose_name=_("Hashed SMB password") ) bsdusr_group = models.ForeignKey( bsdGroups, on_delete=models.SET(get_sentinel_group), verbose_name=_("Primary Group ID") ) bsdusr_home = PathField( default="/nonexistent", verbose_name=_("Home Directory"), includes=["/root", "/nonexistent"], ) bsdusr_shell = models.CharField( max_length=120, default='/bin/csh', verbose_name=_("Shell") ) bsdusr_full_name = models.CharField( max_length=120, verbose_name=_("Full Name") ) bsdusr_builtin = models.BooleanField( default=False, editable=False, verbose_name=_("Built-in User"), ) bsdusr_email = models.EmailField( verbose_name=_("E-mail"), blank=True ) bsdusr_password_disabled = models.BooleanField( verbose_name=_("Disable password login"), default=False, help_text=_( 'This disables all forms of password login, including for sharing.' ), ) bsdusr_locked = models.BooleanField( verbose_name=_("Lock user"), default=False, ) bsdusr_sudo = models.BooleanField( verbose_name=_("Permit Sudo"), default=False, ) bsdusr_microsoft_account = models.BooleanField( verbose_name=_("Microsoft Account"), default=False ) is_active = True is_staff = True objects = UserManager() @classmethod def has_root_password(cls): qs = cls.objects.filter(bsdusr_uid=0).exclude(bsdusr_unixhash='*') return qs.exists() @property def bsdusr_sshpubkey(self): keysfile = '%s/.ssh/authorized_keys' % self.bsdusr_home if not os.path.exists(keysfile): return '' try: with open(keysfile, 'r') as f: keys = f.read() return keys except: return '' class Meta: verbose_name = _("User") verbose_name_plural = _("Users") ordering = ['bsdusr_builtin', 'bsdusr_username'] def __unicode__(self): return self.bsdusr_username def get_username(self): "Return the identifying username for this User" return getattr(self, self.USERNAME_FIELD) def __str__(self): return self.get_username() def natural_key(self): return (self.get_username(),) def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, password): # Django auth backend calls set_password even if user doesnt exist if not self.bsdusr_username or not self.id: time.sleep(0.1) return unixhash, smbhash = notifier().user_changepassword( username=self.bsdusr_username.encode('utf-8'), password=password, ) self.bsdusr_unixhash = unixhash self.bsdusr_smbhash = smbhash def check_password(self, raw_password): # Only allow uid 0 for now if self.bsdusr_uid != 0: return False if self.bsdusr_unixhash: if self.bsdusr_unixhash == 'x' or self.bsdusr_unixhash == '*': return False if isinstance(raw_password, unicode): raw_password = raw_password.encode('utf-8') return crypt.crypt( raw_password, str(self.bsdusr_unixhash) ) == str(self.bsdusr_unixhash) def delete(self, using=None, reload=True): from freenasUI.services.models import CIFS if self.bsdusr_builtin is True: raise ValueError(_( "User %s is built-in and can not be deleted!" ) % (self.bsdusr_username)) notifier().user_deleteuser(self.bsdusr_username.encode('utf-8')) if domaincontroller_enabled(): Samba4().user_delete(self.bsdusr_username.encode('utf-8')) try: gobj = self.bsdusr_group count = bsdGroupMembership.objects.filter( bsdgrpmember_group=gobj).count() count2 = bsdUsers.objects.filter(bsdusr_group=gobj).exclude( id=self.id).count() if not gobj.bsdgrp_builtin and count == 0 and count2 == 0: gobj.delete(reload=False, pwdelete=False) except: pass cifs = CIFS.objects.latest('id') if cifs: if cifs.cifs_srv_guest == self.bsdusr_username: cifs.cifs_srv_guest = 'nobody' cifs.save() super(bsdUsers, self).delete(using) if reload: notifier().reload("user") def save(self, *args, **kwargs): #TODO: Add last_login field if ( 'update_fields' in kwargs and 'last_login' in kwargs['update_fields'] ): kwargs['update_fields'].remove('last_login') super(bsdUsers, self).save(*args, **kwargs)
class bsdUsers(Model): USERNAME_FIELD = 'bsdusr_username' REQUIRED_FIELDS = [] bsdusr_uid = models.IntegerField( verbose_name=_("User ID"), validators=[MinValueValidator(0), MaxValueValidator(4294967295)], ) bsdusr_username = models.CharField( max_length=16, unique=True, default=_('User &'), verbose_name=_("Username") ) bsdusr_unixhash = models.CharField( max_length=128, blank=True, default='*', verbose_name=_("Hashed UNIX password") ) bsdusr_smbhash = models.CharField( max_length=128, blank=True, default='*', verbose_name=_("Hashed SMB password") ) bsdusr_group = models.ForeignKey( bsdGroups, on_delete=models.SET(get_sentinel_group), verbose_name=_("Primary Group ID") ) bsdusr_home = PathField( default="/nonexistent", verbose_name=_("Home Directory"), includes=["/root", "/nonexistent"], ) bsdusr_shell = models.CharField( max_length=120, default='/bin/csh', verbose_name=_("Shell"), choices=choices.SHELL_CHOICES(), ) bsdusr_full_name = models.CharField( max_length=120, verbose_name=_("Full Name") ) bsdusr_builtin = models.BooleanField( default=False, editable=False, verbose_name=_("Built-in User"), ) bsdusr_email = models.EmailField( verbose_name=_("E-mail"), blank=True ) bsdusr_password_disabled = models.BooleanField( verbose_name=_("Disable password login"), default=False, help_text=_( 'This disables all forms of password login, including for sharing.' ), ) bsdusr_locked = models.BooleanField( verbose_name=_("Lock user"), default=False, ) bsdusr_sudo = models.BooleanField( verbose_name=_("Permit Sudo"), default=False, ) bsdusr_microsoft_account = models.BooleanField( verbose_name=_("Microsoft Account"), default=False ) bsdusr_attributes = DictField( default=None, editable=False, ) is_active = True is_staff = True objects = UserManager() @classmethod def has_root_password(cls): qs = cls.objects.filter(bsdusr_uid=0).exclude(bsdusr_unixhash='*') return qs.exists() @property def bsdusr_sshpubkey(self): keysfile = '%s/.ssh/authorized_keys' % self.bsdusr_home if not os.path.exists(keysfile): return '' try: with open(keysfile, 'r') as f: keys = f.read() return keys except: return '' class Meta: verbose_name = _("User") verbose_name_plural = _("Users") ordering = ['bsdusr_builtin', 'bsdusr_username'] def __str__(self): return self.bsdusr_username def get_username(self): "Return the identifying username for this User" return getattr(self, self.USERNAME_FIELD) def natural_key(self): return (self.get_username(),) @property def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False @property def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, password): # Django auth backend calls set_password even if user doesnt exist if not self.bsdusr_username or not self.id: time.sleep(0.1) return with client as c: pk = c.call('user.update', self.id, {'password': password}) user = bsdUsers.objects.get(pk=pk) self.bsdusr_unixhash = user.bsdusr_unixhash self.bsdusr_smbhash = user.bsdusr_smbhash def check_password(self, raw_password): # Only allow uid 0 for now if self.bsdusr_uid != 0: return False if self.bsdusr_unixhash: if self.bsdusr_unixhash == 'x' or self.bsdusr_unixhash == '*': return False if isinstance(raw_password, bytes): raw_password = raw_password.decode('utf-8') return crypt.crypt( raw_password, str(self.bsdusr_unixhash) ) == str(self.bsdusr_unixhash) def save(self, *args, **kwargs): # TODO: Add last_login field if ( 'update_fields' in kwargs and 'last_login' in kwargs['update_fields'] ): kwargs['update_fields'].remove('last_login') super(bsdUsers, self).save(*args, **kwargs)
class Rsync(Model): rsync_path = PathField( verbose_name=_("Path"), abspath=False, ) rsync_remotehost = models.CharField( max_length=120, verbose_name=_("Remote Host"), help_text=_("IP Address or hostname"), ) rsync_remoteport = models.SmallIntegerField( default=22, verbose_name=_("Remote SSH Port"), help_text=_("SSH Port"), validators=[MinValueValidator(1), MaxValueValidator(65535)], ) rsync_mode = models.CharField( max_length=20, choices=choices.RSYNC_MODE_CHOICES, default='module', ) rsync_remotemodule = models.CharField( max_length=120, verbose_name=_("Remote Module Name"), blank=True, help_text=_("Name of the module defined in the remote rsync " "daemon"), ) rsync_remotepath = models.CharField( max_length=120, verbose_name=_("Remote Path"), blank=True, help_text=_("Path on remote host to rsync to, e.g. /mnt/tank"), ) rsync_direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_("Push - From local to remote machine. Pull - From " "remote to local machine."), default='push', choices=choices.RSYNC_DIRECTION, ) rsync_desc = models.CharField( max_length=120, verbose_name=_("Short description"), blank=True, ) rsync_minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_("Values 0-59 allowed."), ) rsync_hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values 0-23 allowed."), ) rsync_daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values 1-31 allowed."), ) rsync_month = models.CharField( max_length=100, default='1,2,3,4,5,6,7,8,9,a,b,c', verbose_name=_("Month"), ) rsync_dayweek = models.CharField( max_length=100, default="1,2,3,4,5,6,7", verbose_name=_("Day of week"), ) rsync_user = UserField( max_length=60, verbose_name=_("User"), help_text=_("The user to run the command"), ) rsync_recursive = models.BooleanField( verbose_name=_("Recursive"), help_text=_("Recurse into directories"), default=True, ) rsync_times = models.BooleanField( verbose_name=_("Times"), help_text=_("Preserve modification times"), default=True, ) rsync_compress = models.BooleanField( verbose_name=_("Compress"), help_text=_("Compress data during the transfer"), default=True, ) rsync_archive = models.BooleanField( verbose_name=_("Archive"), help_text=_("Archive mode"), default=False, ) rsync_delete = models.BooleanField( verbose_name=_("Delete"), help_text=_("Delete files on the receiving side that don't exist " "on sender"), default=False, ) rsync_quiet = models.BooleanField( verbose_name=_("Quiet"), help_text=_("Suppress non-error messages"), default=False, ) rsync_preserveperm = models.BooleanField( verbose_name=_("Preserve permissions"), help_text=_("This option causes the receiving rsync to set the " "destination permissions to be the same as the source " "permissions"), default=False, ) rsync_preserveattr = models.BooleanField( verbose_name=_("Preserve extended attributes"), help_text=_("This option causes rsync to update the remote " "extended attributes to be the same as the local ones"), default=False, ) rsync_extra = models.TextField( verbose_name=_("Extra options"), help_text=_("Extra options to rsync command line (usually empty)"), blank=True) rsync_enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), ) class Meta: verbose_name = _("Rsync Task") verbose_name_plural = _("Rsync Tasks") ordering = ["rsync_path", "rsync_desc"] def __unicode__(self): if self.rsync_desc: return self.rsync_desc elif self.rsync_mode == 'module': return self.rsync_remotemodule else: return self.rsync_remotepath def get_human_minute(self): if self.rsync_minute == '*': return _(u'Every minute') elif self.rsync_minute.startswith('*/'): return _(u'Every %s minute(s)') % self.rsync_minute.split('*/')[1] else: return self.rsync_minute def get_human_hour(self): if self.rsync_hour == '*': return _(u'Every hour') elif self.rsync_hour.startswith('*/'): return _(u'Every %s hour(s)') % self.rsync_hour.split('*/')[1] else: return self.rsync_hour def get_human_daymonth(self): if self.rsync_daymonth == '*': return _(u'Everyday') elif self.rsync_daymonth.startswith('*/'): return _(u'Every %s days') % self.rsync_daymonth.split('*/')[1] else: return self.rsync_daymonth def get_human_month(self): months = self.rsync_month.split(',') if len(months) == 12 or self.rsync_month == '*': return _("Every month") mchoices = dict(choices.MONTHS_CHOICES) labels = [] for m in months: if m in ('10', '11', '12'): m = chr(87 + int(m)) labels.append(unicode(mchoices[m])) return ', '.join(labels) def get_human_dayweek(self): weeks = self.rsync_dayweek.split(',') if len(weeks) == 7 or self.rsync_dayweek == '*': return _('Everyday') if weeks == map(str, xrange(1, 6)): return _('Weekdays') if weeks == map(str, xrange(6, 8)): return _('Weekends') wchoices = dict(choices.WEEKDAYS_CHOICES) labels = [] for w in weeks: labels.append(unicode(wchoices[str(w)])) return ', '.join(labels) def delete(self): super(Rsync, self).delete() try: notifier().restart("cron") except: pass
class AFP_Share(Model): afp_name = models.CharField( max_length=120, verbose_name=_("Name"), help_text= _("The volume name is the name that appears in the Chooser of the 'connect to server' dialog on Macintoshes to represent the appropriate share. If volumename is unspecified, the last component of pathname is used. No two volumes may have the same name. The volume name cannot contain the ':' character. The volume name is mangled if it is very long. Mac codepage volume name is limited to 27 characters. UTF8-MAC volume name is limited to 'Volume Name Length' parameter in Services:Apple Share" )) afp_comment = models.CharField(max_length=120, verbose_name=_("Share Comment"), blank=True) afp_path = PathField(verbose_name=_("Path")) afp_sharepw = models.CharField( max_length=120, verbose_name=_("Share password"), blank=True, help_text= _("This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)." )) afp_allow = models.CharField( max_length=120, verbose_name=_("Allow List"), blank=True, help_text= _("This option allows the users and groups that access a share to be specified. Users and groups are specified, delimited by commas. Groups are designated by a @ prefix." )) afp_deny = models.CharField( max_length=120, verbose_name=_("Deny List"), blank=True, help_text= _("The deny option specifies users and groups who are not allowed access to the share. It follows the same format as the allow option." )) afp_ro = models.CharField( max_length=120, verbose_name=_("Read-only Access"), blank=True, help_text= _("Allows certain users and groups to have read-only access to a share. This follows the allow option format." )) afp_rw = models.CharField( max_length=120, verbose_name=_("Read-write Access"), blank=True, help_text= _("Allows certain users and groups to have read/write access to a share. This follows the allow option format." )) afp_timemachine = models.BooleanField( verbose_name=_("Time Machine"), help_text=_( "Check this to enable Time Machine backups on this share.")) afp_dbpath = models.CharField( max_length=120, verbose_name=_("Database Path"), blank=True, help_text= _("Sets the database information to be stored in path. You have to specify a writable location, even if the volume is read only." )) afp_nodev = models.BooleanField( verbose_name=_("Zero Device Numbers"), help_text= _("Always use 0 for device number, helps when the device number is not constant across a reboot, cluster, ..." )) afp_nostat = models.BooleanField( verbose_name=_("No Stat"), help_text= _("Don't stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script." )) afp_upriv = models.BooleanField(verbose_name=_("AFP3 Unix Privs"), default=True, help_text=_("Use AFP3 unix privileges.")) afp_fperm = models.CharField( max_length=3, default="755", verbose_name=_("Default file permission"), ) afp_dperm = models.CharField( max_length=3, default="644", verbose_name=_("Default directory permission"), ) afp_umask = models.CharField( max_length=3, default="000", verbose_name=_("Default umask"), ) def __unicode__(self): return unicode(self.afp_name) def delete(self, *args, **kwargs): super(AFP_Share, self).delete(*args, **kwargs) notifier().reload("afp") class Meta: verbose_name = _("Apple (AFP) Share") verbose_name_plural = _("Apple (AFP) Shares") ordering = ["afp_name"]
class CloudSync(Model): description = models.CharField( max_length=150, verbose_name=_('Description'), ) direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_("Push - From local to remote. Pull - From " "remote to local."), default='PUSH', choices=( ('PUSH', _('Push')), ('PULL', _('Pull')), )) path = PathField( verbose_name=_("Path"), abspath=False, ) credential = models.ForeignKey( 'system.CloudCredentials', verbose_name=_('Credential'), ) transfer_mode = models.CharField( verbose_name=_('Transfer Mode'), default='sync', choices=( ('SYNC', _('Sync')), ('COPY', _('Copy')), ('MOVE', _('Move')), ), max_length=20, ) attributes = DictField(editable=False, ) encryption = models.BooleanField( verbose_name=_("Remote encryption"), help_text=_( "This option will push encrypted files and decrypt pulled files.", ), ) filename_encryption = models.BooleanField( default=True, verbose_name=_("Filename encryption"), help_text=_("Also encrypt filenames.", ), ) encryption_password = models.CharField( blank=True, max_length=256, verbose_name=_("Encryption password"), ) encryption_salt = models.CharField( blank=True, max_length=256, verbose_name=_("Encryption salt"), ) minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_("Values allowed:" "<br>Slider: 0-30 (as it is every Nth minute)." "<br>Specific Minute: 0-59."), ) hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values allowed:" "<br>Slider: 0-12 (as it is every Nth hour)." "<br>Specific Hour: 0-23."), ) daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values allowed:" "<br>Slider: 0-15 (as its is every Nth day)." "<br>Specific Day: 1-31."), ) month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), help_text=_( 'Disabling will not stop any syncs which are in progress.'), ) class Meta: verbose_name = _("Cloud Sync") verbose_name_plural = _("Cloud Syncs") ordering = ["description"] def __str__(self): return self.description def get_human_minute(self): if self.minute == '*': return _('Every minute') elif self.minute.startswith('*/'): return _('Every {0} minute(s)').format(self.minute.split('*/')[1]) else: return self.minute def get_human_hour(self): if self.hour == '*': return _('Every hour') elif self.hour.startswith('*/'): return _('Every {0} hour(s)').format(self.hour.split('*/')[1]) else: return self.hour def get_human_daymonth(self): if self.daymonth == '*': return _('Everyday') elif self.daymonth.startswith('*/'): return _('Every {0} days').format(self.daymonth.split('*/')[1]) else: return self.daymonth def get_human_month(self): months = self.month.split(',') if len(months) == 12 or self.month == '*': return _("Every month") mchoices = dict(choices.MONTHS_CHOICES) labels = [] for m in months: labels.append(str(mchoices[m])) return ', '.join(labels) def get_human_dayweek(self): weeks = self.dayweek.split(',') if len(weeks) == 7 or self.dayweek == '*': return _('Everyday') if weeks == list(map(str, range(1, 6))): return _('Weekdays') if weeks == list(map(str, range(6, 8))): return _('Weekends') wchoices = dict(choices.WEEKDAYS_CHOICES) labels = [] for w in weeks: labels.append(str(wchoices[str(w)])) return ', '.join(labels) def run(self): with client as c: jid = c.call('cloudsync.sync', self.id) return jid
class AFP_Share(Model): afp_name = models.CharField( max_length=120, verbose_name=_("Name"), help_text= _("The volume name is the name that appears in the Chooser of the 'connect to server' dialog on Macintoshes to represent the appropriate share. If volumename is unspecified, the last component of pathname is used. No two volumes may have the same name. The volume name cannot contain the ':' character. The volume name is mangled if it is very long. Mac codepage volume name is limited to 27 characters. UTF8-MAC volume name is limited to 'Volume Name Length' parameter in Services:Apple Share" )) afp_comment = models.CharField(max_length=120, verbose_name=_("Share Comment"), blank=True) afp_path = PathField(verbose_name=_("Path")) afp_sharepw = models.CharField( max_length=120, verbose_name=_("Share password"), blank=True, help_text= _("This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)." )) afp_sharecharset = models.CharField( max_length=120, verbose_name=_("Share Character Set"), blank=True, help_text= _("Specifies the share character set. For example UTF8, UTF8-MAC, ISO-8859-15, etc." )) afp_allow = models.CharField( max_length=120, verbose_name=_("Allow List"), blank=True, help_text= _("This option allows the users and groups that access a share to be specified. Users and groups are specified, delimited by commas. Groups are designated by a @ prefix." )) afp_deny = models.CharField( max_length=120, verbose_name=_("Deny List"), blank=True, help_text= _("The deny option specifies users and groups who are not allowed access to the share. It follows the same format as the allow option." )) afp_ro = models.CharField( max_length=120, verbose_name=_("Read-only Access"), blank=True, help_text= _("Allows certain users and groups to have read-only access to a share. This follows the allow option format." )) afp_rw = models.CharField( max_length=120, verbose_name=_("Read-write Access"), blank=True, help_text= _("Allows certain users and groups to have read/write access to a share. This follows the allow option format." )) afp_diskdiscovery = models.BooleanField( verbose_name=_("Disk Discovery"), help_text= _("Allow other systems to discover this share as a disk for data, as a Time Machine backup volume or not at all." )) afp_discoverymode = models.CharField( max_length=120, choices=choices.DISKDISCOVERY_CHOICES, default='Default', verbose_name=_("Disk discovery mode"), help_text= _("Note! Selecting 'Time Machine' on multiple shares may cause unpredictable behavior in MacOS. Default mode exports the volume as a data volume for users." )) afp_dbpath = models.CharField( max_length=120, verbose_name=_("Database Path"), blank=True, help_text= _("Sets the database information to be stored in path. You have to specify a writable location, even if the volume is read only." )) afp_cachecnid = models.BooleanField( verbose_name=_("Cache CNID"), help_text= _("If set afpd uses the ID information stored in AppleDouble V2 header files to reduce database load. Don't set this option if the volume is modified by non AFP clients (NFS/SMB/local)." )) afp_crlf = models.BooleanField( verbose_name=_("Translate CR/LF"), help_text= _("Enables crlf translation for TEXT files, automatically converting macintosh line breaks into Unix ones. Use of this option might be dangerous since some older programs store binary data files as type 'TEXT' when saving and switch the filetype in a second step. Afpd will potentially destroy such files when 'erroneously' changing bytes in order to do line break translation." )) afp_mswindows = models.BooleanField( verbose_name=_("Windows File Names"), help_text= _("This forces filenames to be restricted to the character set used by Windows. This is not recommended for shares used principally by Mac computers." )) afp_adouble = models.BooleanField( verbose_name=_("Enable .AppleDouble"), help_text=_("This will enable automatic creation of the " ".AppleDouble directories. This option should be used if " "files are accessed by Mac computers."), default=True, ) afp_nodev = models.BooleanField( verbose_name=_("Zero Device Numbers"), help_text= _("Always use 0 for device number, helps when the device number is not constant across a reboot, cluster, ..." )) afp_nofileid = models.BooleanField( verbose_name=_("Disable File ID"), help_text=_( "Don't advertise createfileid, resolveid, deleteid calls.")) afp_nohex = models.BooleanField( verbose_name=_("Disable :hex Names"), help_text= _("Disable :hex translations for anything except dot files. This option makes the '/' character illegal." )) afp_prodos = models.BooleanField( verbose_name=_("ProDOS"), help_text=_("Provide compatibility with Apple II clients.")) afp_nostat = models.BooleanField( verbose_name=_("No Stat"), help_text= _("Don't stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script." )) afp_upriv = models.BooleanField(verbose_name=_("AFP3 Unix Privs"), default=True, help_text=_("Use AFP3 unix privileges.")) afp_fperm = models.CharField( max_length=3, default="755", verbose_name=_("Default file permission"), ) afp_dperm = models.CharField( max_length=3, default="644", verbose_name=_("Default directory permission"), ) def __unicode__(self): return unicode(self.afp_name) def delete(self, *args, **kwargs): super(AFP_Share, self).delete(*args, **kwargs) notifier().reload("afp") class Meta: verbose_name = _("Apple (AFP) Share") verbose_name_plural = _("Apple (AFP) Shares") ordering = ["afp_name"] class FreeAdmin: icon_model = u"AppleShareIcon" icon_add = u"AddAppleShareIcon" icon_view = u"ViewAllAppleSharesIcon" icon_object = u"AppleShareIcon" advanced_fields = ( 'afp_cachecnid', 'afp_sharecharset', 'afp_nofileid', 'afp_nodev', 'afp_nohex', 'afp_prodos', 'afp_nostat', )
class AFP_Share(Model): afp_path = PathField( verbose_name=_("Path"), ) afp_home = models.BooleanField( verbose_name=_('Use as home share'), default=False, ) afp_name = models.CharField( max_length=120, verbose_name=_("Name"), help_text=_( "The volume name is the name that appears in the Chooser of the " "'connect to server' dialog on Macintoshes to represent the " "appropriate share. If volumename is unspecified, the last " "component of pathname is used. No two volumes may have the same " "name. The volume name cannot contain the ':' character. The " "volume name is mangled if it is very long. Mac codepage volume " "name is limited to 27 characters. UTF8-MAC volume name is limited" " to 'Volume Name Length' parameter in Services:Apple Share" ), ) afp_comment = models.CharField( max_length=120, verbose_name=_("Share Comment"), blank=True ) afp_allow = models.CharField( max_length=120, verbose_name=_("Allow List"), blank=True, help_text=_( "This option allows the users and groups that access a share to " "be specified. Users and groups are specified, delimited by " "commas. Groups are designated by a @ prefix." ), ) afp_deny = models.CharField( max_length=120, verbose_name=_("Deny List"), blank=True, help_text=_( "The deny option specifies users and groups who are not allowed " "access to the share. It follows the same format as the allow " "option." ), ) afp_ro = models.CharField( max_length=120, verbose_name=_("Read-only Access"), blank=True, help_text=_( "Allows certain users and groups to have read-only access to a " "share. This follows the allow option format." ), ) afp_rw = models.CharField( max_length=120, verbose_name=_("Read-write Access"), blank=True, help_text=_( "Allows certain users and groups to have read/write access to a " "share. This follows the allow option format." ), ) afp_timemachine = models.BooleanField( verbose_name=_('Time Machine'), help_text=_( 'Check this to enable Time Machine backups on this share.' ), default=False, ) afp_timemachine_quota = models.IntegerField( verbose_name=_('Time Machine Quota, GiB'), help_text=_( 'Quota for each Time Machine backup on this share (in GiB). ' 'Please note that this change will be applied only after ' 'share re-mount.' ), default=0, ) afp_nodev = models.BooleanField( verbose_name=_('Zero Device Numbers'), help_text=_( 'Always use 0 for device number, helps when the device number is ' 'not constant across a reboot, cluster, ...' ), default=False, ) afp_nostat = models.BooleanField( verbose_name=_('No Stat'), help_text=_( 'Don\'t stat volume path when enumerating volumes list, useful ' 'for automounting or volumes created by a preexec script.' ), default=False, ) afp_upriv = models.BooleanField( verbose_name=_('AFP3 Unix Privs'), help_text=_('Use AFP3 unix privileges.'), default=True, ) afp_fperm = models.CharField( max_length=3, default="644", verbose_name=_("Default file permission"), ) afp_dperm = models.CharField( max_length=3, default="755", verbose_name=_("Default directory permission"), ) afp_umask = models.CharField( max_length=3, default="000", blank=True, verbose_name=_("Default umask"), ) afp_hostsallow = models.CharField( blank=True, max_length=120, help_text=_( "Allow only listed hosts and/or networks access to this volume" ), verbose_name=_("Hosts Allow") ) afp_hostsdeny = models.CharField( blank=True, max_length=120, help_text=_("Deny listed hosts and/or networks access to this volume"), verbose_name=_("Hosts Deny") ) afp_vuid = models.CharField( max_length=36, verbose_name=_('vuid for Time Machine'), blank=True, editable=False, ) afp_auxparams = models.TextField( blank=True, max_length=255, help_text=_( "These parameters are added to the [Volume] section of afp.conf." " Add each different parameter on a newline" ), verbose_name=_("Auxiliary Parameters") ) afp_enabled = models.BooleanField( verbose_name=_("Enabled"), default=True, ) def __str__(self): return str(self.afp_name) class Meta: verbose_name = _("Apple (AFP) Share") verbose_name_plural = _("Apple (AFP) Shares") ordering = ["afp_name"]
class AFP_Share(Model): afp_path = PathField(verbose_name=_("Path"), ) afp_name = models.CharField( max_length=120, verbose_name=_("Name"), help_text=_( "The volume name is the name that appears in the Chooser of the " "'connect to server' dialog on Macintoshes to represent the " "appropriate share. If volumename is unspecified, the last " "component of pathname is used. No two volumes may have the same " "name. The volume name cannot contain the ':' character. The " "volume name is mangled if it is very long. Mac codepage volume " "name is limited to 27 characters. UTF8-MAC volume name is limited" " to 'Volume Name Length' parameter in Services:Apple Share"), ) afp_comment = models.CharField(max_length=120, verbose_name=_("Share Comment"), blank=True) afp_allow = models.CharField( max_length=120, verbose_name=_("Allow List"), blank=True, help_text=_( "This option allows the users and groups that access a share to " "be specified. Users and groups are specified, delimited by " "commas. Groups are designated by a @ prefix."), ) afp_deny = models.CharField( max_length=120, verbose_name=_("Deny List"), blank=True, help_text=_( "The deny option specifies users and groups who are not allowed " "access to the share. It follows the same format as the allow " "option."), ) afp_ro = models.CharField( max_length=120, verbose_name=_("Read-only Access"), blank=True, help_text=_( "Allows certain users and groups to have read-only access to a " "share. This follows the allow option format."), ) afp_rw = models.CharField( max_length=120, verbose_name=_("Read-write Access"), blank=True, help_text=_( "Allows certain users and groups to have read/write access to a " "share. This follows the allow option format."), ) afp_timemachine = models.BooleanField( verbose_name=_('Time Machine'), help_text=_( 'Check this to enable Time Machine backups on this share.'), default=False, ) afp_nodev = models.BooleanField( verbose_name=_('Zero Device Numbers'), help_text=_( 'Always use 0 for device number, helps when the device number is ' 'not constant across a reboot, cluster, ...'), default=False, ) afp_nostat = models.BooleanField( verbose_name=_('No Stat'), help_text=_( 'Don\'t stat volume path when enumerating volumes list, useful ' 'for automounting or volumes created by a preexec script.'), default=False, ) afp_upriv = models.BooleanField( verbose_name=_('AFP3 Unix Privs'), help_text=_('Use AFP3 unix privileges.'), default=True, ) afp_fperm = models.CharField( max_length=3, default="644", verbose_name=_("Default file permission"), ) afp_dperm = models.CharField( max_length=3, default="755", verbose_name=_("Default directory permission"), ) afp_umask = models.CharField( max_length=3, default="000", blank=True, verbose_name=_("Default umask"), ) afp_hostsallow = models.CharField( blank=True, max_length=120, help_text=_( "Allow only listed hosts and/or networks access to this volume"), verbose_name=_("Hosts Allow")) afp_hostsdeny = models.CharField( blank=True, max_length=120, help_text=_("Deny listed hosts and/or networks access to this volume"), verbose_name=_("Hosts Deny")) def __unicode__(self): return unicode(self.afp_name) def delete(self, *args, **kwargs): super(AFP_Share, self).delete(*args, **kwargs) notifier().reload("afp") class Meta: verbose_name = _("Apple (AFP) Share") verbose_name_plural = _("Apple (AFP) Shares") ordering = ["afp_name"]
class CIFS_Share(Model): cifs_path = PathField( verbose_name=_("Path"), blank=True, ) cifs_home = models.BooleanField( verbose_name=_('Use as home share'), default=False, ) cifs_timemachine = models.BooleanField( verbose_name=_('Time Machine'), help_text=_( 'Enable Time Machine backups on this share.' ), default=False, ) cifs_name = models.CharField( max_length=120, verbose_name=_("Name") ) cifs_comment = models.CharField( max_length=120, verbose_name=_("Comment"), blank=True, ) cifs_ro = models.BooleanField( verbose_name=_('Export Read Only'), default=False, ) cifs_browsable = models.BooleanField( verbose_name=_('Browsable to Network Clients'), default=True, ) cifs_recyclebin = models.BooleanField( verbose_name=_('Export Recycle Bin'), default=False, ) cifs_showhiddenfiles = models.BooleanField( verbose_name=_('Show Hidden Files'), default=False, ) cifs_shadowcopy = models.BooleanField( verbose_name=_('Enable Shadow Copies'), help_text=_( 'Export ZFS snapshots as Shadow Copies for VSS clients.' ), default=False ) cifs_guestok = models.BooleanField( verbose_name=_('Allow Guest Access'), help_text=_( 'If true then no password is required to connect to the share. ' 'Privileges will be those of the guest account.' ), default=False, ) cifs_guestonly = models.BooleanField( verbose_name=_('Only Allow Guest Access'), help_text=_( 'If true then only guest connections to the share are permitted. ' 'This parameter will have no effect if Allow Guest Access is not ' 'set for the share.' ), default=False, ) cifs_abe = models.BooleanField( verbose_name=_('Access Based Share Enumeration'), default=False ) cifs_hostsallow = models.TextField( blank=True, verbose_name=_("Hosts Allow"), help_text=_( "This option is a comma, space, or tab delimited set of hosts " "which are permitted to access this share. You can specify the " "hosts by name or IP number. Leave this field empty to use " "default settings." ), ) cifs_hostsdeny = models.TextField( blank=True, verbose_name=_("Hosts Deny"), help_text=_( "This option is a comma, space, or tab delimited set of host " "which are NOT permitted to access this share. Where the lists " "conflict, the allow list takes precedence. In the event that it " "is necessary to deny all by default, use the keyword ALL (or the " "netmask 0.0.0.0/0) and then explicitly specify to the hosts " "allow parameter those hosts that should be permitted access. " "Leave this field empty to use default settings." ), ) cifs_vfsobjects = MultiSelectField( verbose_name=_('VFS Objects'), max_length=255, blank=True, default='zfs_space,zfsacl,streams_xattr', choices=list(choices.CIFS_VFS_OBJECTS()) ) cifs_vuid = models.CharField( max_length=36, verbose_name=_('vuid for Time Machine'), blank=True, editable=False, ) cifs_auxsmbconf = models.TextField( verbose_name=_("Auxiliary Parameters"), blank=True, help_text=_( "These parameters are added to [Share] section of smb.conf" ) ) def __str__(self): return self.cifs_name class Meta: verbose_name = _("Windows (SMB) Share") verbose_name_plural = _("Windows (SMB) Shares") ordering = ["cifs_name"]
class CIFS_Share(Model): cifs_name = models.CharField(max_length=120, verbose_name=_("Name")) cifs_comment = models.CharField( max_length=120, verbose_name=_("Comment"), blank=True, ) cifs_path = PathField(verbose_name=_("Path")) cifs_default_permissions = models.BooleanField( verbose_name=_('Apply Default Permissions'), help_text=_( 'Recursively set sane default windows permissions on share'), default=True) cifs_ro = models.BooleanField( verbose_name=_('Export Read Only'), default=False, ) cifs_browsable = models.BooleanField( verbose_name=_('Browsable to Network Clients'), default=True, ) cifs_recyclebin = models.BooleanField( verbose_name=_('Export Recycle Bin'), default=False, ) cifs_showhiddenfiles = models.BooleanField( verbose_name=_('Show Hidden Files'), default=False, ) cifs_guestok = models.BooleanField( verbose_name=_('Allow Guest Access'), help_text=_( 'If true then no password is required to connect to the share. ' 'Privileges will be those of the guest account.'), default=False, ) cifs_guestonly = models.BooleanField( verbose_name=_('Only Allow Guest Access'), help_text=_( 'If true then only guest connections to the share are permitted. ' 'This parameter will have no effect if Allow Guest Access is not ' 'set for the share.'), default=False, ) cifs_inheritowner = models.BooleanField( verbose_name=_("Inherit Owner"), help_text= _("The ownership of new files and directories is normally governed by effective uid of the connected user. This option allows the Samba administrator to specify that the ownership for new files and directories should be controlled by the ownership of the parent directory. For windows shares it is NOT recommended to set this. The desired behavior can be achieved using ACL's. This probably won't do what you want it to do." ), default=False) cifs_inheritperms = models.BooleanField( verbose_name=_("Inherit Permissions"), help_text= _("New directories inherit the mode of the parent directory, including bits such as setgid. New files inherit their read/write bits from the parent directory. For windows shares it is NOT recommended to set this. The desired behavior can be achieved using ACL's. This probably won't do what you want it to do." ), default=False) cifs_inheritacls = models.BooleanField( verbose_name=_("Inherit ACL's"), help_text= _("This parameter can be used to ensure that if default acls exist on parent directories, they are always honored when creating a new file or subdirectory in these parent directories. The default behavior is to use the unix mode specified when creating the directory. For windows shares it is NOT recommended to set this. The desired behavior can be achieved using ACL's. This probably won't do what you want it to do." ), default=False) cifs_hostsallow = models.TextField( blank=True, verbose_name=_("Hosts Allow"), help_text= _("This option is a comma, space, or tab delimited set of hosts which are permitted to access this share. You can specify the hosts by name or IP number. Leave this field empty to use default settings." )) cifs_hostsdeny = models.TextField( blank=True, verbose_name=_("Hosts Deny"), help_text= _("This option is a comma, space, or tab delimited set of host which are NOT permitted to access this share. Where the lists conflict, the allow list takes precedence. In the event that it is necessary to deny all by default, use the keyword ALL (or the netmask 0.0.0.0/0) and then explicitly specify to the hosts allow parameter those hosts that should be permitted access. Leave this field empty to use default settings." )) cifs_auxsmbconf = models.TextField( max_length=120, verbose_name=_("Auxiliary Parameters"), blank=True, help_text=_( "These parameters are added to [Share] section of smb.conf")) def __unicode__(self): return self.cifs_name def delete(self, *args, **kwargs): super(CIFS_Share, self).delete(*args, **kwargs) notifier().reload("cifs") class Meta: verbose_name = _("Windows (CIFS) Share") verbose_name_plural = _("Windows (CIFS) Shares") ordering = ["cifs_name"]
class bsdUsers(Model): bsdusr_uid = models.IntegerField(verbose_name=_("User ID")) bsdusr_username = models.CharField(max_length=16, unique=True, default=_('User &'), verbose_name=_("Username")) bsdusr_unixhash = models.CharField(max_length=128, blank=True, default='*', verbose_name=_("Hashed UNIX password")) bsdusr_smbhash = models.CharField(max_length=128, blank=True, default='*', verbose_name=_("Hashed SMB password")) bsdusr_group = models.ForeignKey(bsdGroups, on_delete=models.SET(get_sentinel_group), verbose_name=_("Primary Group ID")) bsdusr_home = PathField( default="/nonexistent", verbose_name=_("Home Directory"), includes=["/root", "/nonexistent"], ) bsdusr_shell = models.CharField(max_length=120, default='/bin/csh', verbose_name=_("Shell")) bsdusr_full_name = models.CharField(max_length=120, verbose_name=_("Full Name")) bsdusr_builtin = models.BooleanField( default=False, editable=False, verbose_name=_("Built-in User"), ) bsdusr_email = models.EmailField(verbose_name=_("E-mail"), blank=True) bsdusr_password_disabled = models.BooleanField( verbose_name=_("Disable password login"), default=False, ) bsdusr_locked = models.BooleanField( verbose_name=_("Lock user"), default=False, ) class Meta: verbose_name = _("User") verbose_name_plural = _("Users") def __unicode__(self): return self.bsdusr_username def delete(self, using=None, reload=True): if self.bsdusr_builtin == True: raise ValueError( _("User %s is built-in and can not be " "deleted!") % (self.bsdusr_username)) notifier().user_deleteuser(self.bsdusr_username.encode('utf-8')) try: gobj = self.bsdusr_group count = bsdGroupMembership.objects.filter( bsdgrpmember_group=gobj).count() count2 = bsdUsers.objects.filter(bsdusr_group=gobj).exclude( id=self.id).count() if not gobj.bsdgrp_builtin and count == 0 and count2 == 0: gobj.delete(reload=False) except: pass super(bsdUsers, self).delete(using) if reload: notifier().reload("user")
class bsdUsers(Model): USERNAME_FIELD = 'bsdusr_username' REQUIRED_FIELDS = [] bsdusr_uid = models.IntegerField(verbose_name=_("User ID")) bsdusr_username = models.CharField(max_length=16, unique=True, default=_('User &'), verbose_name=_("Username")) bsdusr_unixhash = models.CharField(max_length=128, blank=True, default='*', verbose_name=_("Hashed UNIX password")) bsdusr_smbhash = models.CharField(max_length=128, blank=True, default='*', verbose_name=_("Hashed SMB password")) bsdusr_group = models.ForeignKey(bsdGroups, on_delete=models.SET(get_sentinel_group), verbose_name=_("Primary Group ID")) bsdusr_home = PathField( default="/nonexistent", verbose_name=_("Home Directory"), includes=["/root", "/nonexistent"], ) bsdusr_shell = models.CharField(max_length=120, default='/bin/csh', verbose_name=_("Shell")) bsdusr_full_name = models.CharField(max_length=120, verbose_name=_("Full Name")) bsdusr_builtin = models.BooleanField( default=False, editable=False, verbose_name=_("Built-in User"), ) bsdusr_email = models.EmailField(verbose_name=_("E-mail"), blank=True) bsdusr_password_disabled = models.BooleanField( verbose_name=_("Disable password login"), default=False, ) bsdusr_locked = models.BooleanField( verbose_name=_("Lock user"), default=False, ) bsdusr_sudo = models.BooleanField( verbose_name=_("Permit Sudo"), default=False, ) is_active = True is_staff = True objects = UserManager() class Meta: verbose_name = _("User") verbose_name_plural = _("Users") ordering = ['bsdusr_builtin'] def __unicode__(self): return self.bsdusr_username def get_username(self): "Return the identifying username for this User" return getattr(self, self.USERNAME_FIELD) def __str__(self): return self.get_username() def natural_key(self): return (self.get_username(), ) def is_anonymous(self): """ Always returns False. This is a way of comparing User objects to anonymous users. """ return False def is_authenticated(self): """ Always return True. This is a way to tell if the user has been authenticated in templates. """ return True def set_password(self, password): """stub required for the API""" pass def check_password(self, raw_password): # Only allow uid 0 for now if self.bsdusr_uid != 0: return False if self.bsdusr_unixhash: if self.bsdusr_unixhash == 'x' or self.bsdusr_unixhash == '*': return False return crypt.crypt(raw_password, str(self.bsdusr_unixhash)) == str( self.bsdusr_unixhash) def delete(self, using=None, reload=True): if self.bsdusr_builtin is True: raise ValueError( _("User %s is built-in and can not be deleted!") % (self.bsdusr_username)) notifier().user_deleteuser(self.bsdusr_username.encode('utf-8')) try: gobj = self.bsdusr_group count = bsdGroupMembership.objects.filter( bsdgrpmember_group=gobj).count() count2 = bsdUsers.objects.filter(bsdusr_group=gobj).exclude( id=self.id).count() if not gobj.bsdgrp_builtin and count == 0 and count2 == 0: gobj.delete(reload=False) except: pass super(bsdUsers, self).delete(using) if reload: notifier().reload("user") def save(self, *args, **kwargs): #TODO: Add last_login field if ('update_fields' in kwargs and 'last_login' in kwargs['update_fields']): kwargs['update_fields'].remove('last_login') super(bsdUsers, self).save(*args, **kwargs)
class CIFS_Share(Model): cifs_path = PathField( verbose_name=_("Path"), blank=True, ) cifs_home = models.BooleanField( verbose_name=_('Use as home share'), default=False, ) cifs_name = models.CharField(max_length=120, verbose_name=_("Name")) cifs_comment = models.CharField( max_length=120, verbose_name=_("Comment"), blank=True, ) cifs_default_permissions = models.BooleanField( verbose_name=_('Apply Default Permissions'), help_text=_( 'Recursively set sane default windows permissions on share'), default=True) cifs_ro = models.BooleanField( verbose_name=_('Export Read Only'), default=False, ) cifs_browsable = models.BooleanField( verbose_name=_('Browsable to Network Clients'), default=True, ) cifs_recyclebin = models.BooleanField( verbose_name=_('Export Recycle Bin'), default=False, ) cifs_showhiddenfiles = models.BooleanField( verbose_name=_('Show Hidden Files'), default=False, ) cifs_guestok = models.BooleanField( verbose_name=_('Allow Guest Access'), help_text=_( 'If true then no password is required to connect to the share. ' 'Privileges will be those of the guest account.'), default=False, ) cifs_guestonly = models.BooleanField( verbose_name=_('Only Allow Guest Access'), help_text=_( 'If true then only guest connections to the share are permitted. ' 'This parameter will have no effect if Allow Guest Access is not ' 'set for the share.'), default=False, ) cifs_hostsallow = models.TextField( blank=True, verbose_name=_("Hosts Allow"), help_text=_( "This option is a comma, space, or tab delimited set of hosts " "which are permitted to access this share. You can specify the " "hosts by name or IP number. Leave this field empty to use " "default settings."), ) cifs_hostsdeny = models.TextField( blank=True, verbose_name=_("Hosts Deny"), help_text=_( "This option is a comma, space, or tab delimited set of host " "which are NOT permitted to access this share. Where the lists " "conflict, the allow list takes precedence. In the event that it " "is necessary to deny all by default, use the keyword ALL (or the " "netmask 0.0.0.0/0) and then explicitly specify to the hosts " "allow parameter those hosts that should be permitted access. " "Leave this field empty to use default settings."), ) cifs_vfsobjects = MultiSelectField(verbose_name=_('VFS Objects'), max_length=255, blank=True, default='aio_pthread,streams_xattr', choices=list( choices.CIFS_VFS_OBJECTS())) cifs_storage_task = models.ForeignKey( Task, verbose_name=_("Periodic Snapshot Task"), on_delete=models.SET_NULL, blank=True, null=True) cifs_auxsmbconf = models.TextField( max_length=120, verbose_name=_("Auxiliary Parameters"), blank=True, help_text=_( "These parameters are added to [Share] section of smb.conf")) def __unicode__(self): return self.cifs_name def delete(self, *args, **kwargs): super(CIFS_Share, self).delete(*args, **kwargs) notifier().reload("cifs") class Meta: verbose_name = _("Windows (CIFS) Share") verbose_name_plural = _("Windows (CIFS) Shares") ordering = ["cifs_name"]
class Rsync(Model): rsync_path = PathField( verbose_name=_("Path"), abspath=False, ) rsync_remotehost = models.CharField( max_length=120, verbose_name=_("Remote Host"), help_text=_("IP Address or hostname. " "Specify user@hostname or user@ip-address " "if your remote machine user and above rsync " "task user are different." ), ) rsync_remoteport = models.SmallIntegerField( default=22, verbose_name=_("Remote SSH Port"), help_text=_("SSH Port"), validators=[MinValueValidator(1), MaxValueValidator(65535)], ) rsync_mode = models.CharField( max_length=20, choices=choices.RSYNC_MODE_CHOICES, default='module', ) rsync_remotemodule = models.CharField( max_length=120, verbose_name=_("Remote Module Name"), blank=True, help_text=_("Name of the module defined in the remote rsync daemon"), ) rsync_remotepath = models.CharField( max_length=255, verbose_name=_("Remote Path"), blank=True, help_text=_("Path on remote host to rsync to, e.g. /mnt/tank"), ) rsync_direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_( "Push - From local to remote machine. Pull - From " "remote to local machine." ), default='push', choices=choices.RSYNC_DIRECTION, ) rsync_desc = models.CharField( max_length=120, verbose_name=_("Short description"), blank=True, ) rsync_minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_( "Values allowed:" "<br>Slider: 0-30 (as it is every Nth minute)." "<br>Specific Minute: 0-59."), ) rsync_hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values allowed:" "<br>Slider: 0-12 (as it is every Nth hour)." "<br>Specific Hour: 0-23."), ) rsync_daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values allowed:" "<br>Slider: 0-15 (as its is every Nth day)." "<br>Specific Day: 1-31."), ) rsync_month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) rsync_dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) rsync_user = UserField( max_length=60, verbose_name=_("User"), help_text=_("The user to run the command"), ) rsync_recursive = models.BooleanField( verbose_name=_("Recursive"), help_text=_("Recurse into directories"), default=True, ) rsync_times = models.BooleanField( verbose_name=_("Times"), help_text=_("Preserve modification times"), default=True, ) rsync_compress = models.BooleanField( verbose_name=_("Compress"), help_text=_("Compress data during the transfer"), default=True, ) rsync_archive = models.BooleanField( verbose_name=_("Archive"), help_text=_("Archive mode"), default=False, ) rsync_delete = models.BooleanField( verbose_name=_("Delete"), help_text=_( "Delete files on the receiving side that don't exist on sender" ), default=False, ) rsync_quiet = models.BooleanField( verbose_name=_("Quiet"), help_text=_("Suppress non-error messages"), default=False, ) rsync_preserveperm = models.BooleanField( verbose_name=_("Preserve permissions"), help_text=_( "This option causes the receiving rsync to set the " "destination permissions to be the same as the source " "permissions" ), default=False, ) rsync_preserveattr = models.BooleanField( verbose_name=_("Preserve extended attributes"), help_text=_( "This option causes rsync to update the remote " "extended attributes to be the same as the local ones" ), default=False, ) rsync_delayupdates = models.BooleanField( verbose_name=_("Delay Updates"), help_text=_("Put all updated files into place at the end"), default=True, ) rsync_extra = models.TextField( verbose_name=_("Extra options"), help_text=_("Extra options to rsync command line (usually empty)"), blank=True ) rsync_enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), ) class Meta: verbose_name = _("Rsync Task") verbose_name_plural = _("Rsync Tasks") ordering = ["rsync_path", "rsync_desc"] def __str__(self): if self.rsync_desc: return self.rsync_desc elif self.rsync_mode == 'module': return self.rsync_remotemodule else: return self.rsync_remotepath
class CloudSync(Model): description = models.CharField( max_length=150, verbose_name=_('Description'), ) direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_( "Push - From local to remote. Pull - From " "remote to local." ), default='PUSH', choices=( ('PUSH', _('Push')), ('PULL', _('Pull')), ) ) path = PathField( verbose_name=_("Path"), abspath=False, ) credential = models.ForeignKey( 'system.CloudCredentials', verbose_name=_('Credential'), ) transfer_mode = models.CharField( verbose_name=_('Transfer Mode'), default='sync', choices=( ('SYNC', _('Sync')), ('COPY', _('Copy')), ('MOVE', _('Move')), ), max_length=20, ) attributes = DictField( editable=False, ) snapshot = models.BooleanField( verbose_name=_("Take snapshot"), help_text=_( "Take dataset snapshot before pushing data.", ), ) pre_script = models.TextField( blank=True, verbose_name=_("Pre-script"), help_text=_( "Script to execute before running sync.", ), ) post_script = models.TextField( blank=True, verbose_name=_("Post-script"), help_text=_( "Script to execute after running sync.", ), ) encryption = models.BooleanField( verbose_name=_("Remote encryption"), help_text=_( "This option will push encrypted files and decrypt pulled files.", ), ) filename_encryption = models.BooleanField( default=True, verbose_name=_("Filename encryption"), help_text=_( "Also encrypt filenames.", ), ) encryption_password = models.CharField( blank=True, max_length=256, verbose_name=_("Encryption password"), ) encryption_salt = models.CharField( blank=True, max_length=256, verbose_name=_("Encryption salt"), ) args = models.TextField( blank=True, max_length=255, verbose_name=_("Auxiliary arguments"), help_text=_( "These arguments will be passed to rclone." "<br>See <a href=\"https://rclone.org/docs/\">https://rclone.org/docs/</a> for help"), ) minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_( "Values allowed:" "<br>Slider: 0-30 (as it is every Nth minute)." "<br>Specific Minute: 0-59."), ) hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values allowed:" "<br>Slider: 0-12 (as it is every Nth hour)." "<br>Specific Hour: 0-23."), ) daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values allowed:" "<br>Slider: 0-15 (as its is every Nth day)." "<br>Specific Day: 1-31."), ) month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) follow_symlinks = models.BooleanField( verbose_name=_("Follow symlinks"), help_text=_( "Follow symlinks and copy the pointed to item.", ), ) transfers = models.IntegerField( null=True, ) bwlimit = ListField( editable=False, ) exclude = ListField( editable=False, ) enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), help_text=_( 'Disabling will not stop any syncs which are in progress.' ), ) class Meta: verbose_name = _("Cloud Sync") verbose_name_plural = _("Cloud Syncs") ordering = ["description"] def __str__(self): return self.description def run(self): with client as c: jid = c.call('cloudsync.sync', self.id) return jid
class Rsync(Model): rsync_path = PathField( verbose_name=_("Path"), abspath=False, ) rsync_remotehost = models.CharField( max_length=120, verbose_name=_("Remote Host"), help_text=_("IP Address or hostname"), ) rsync_remoteport = models.SmallIntegerField( default=22, verbose_name=_("Remote SSH Port"), help_text=_("SSH Port"), validators=[MinValueValidator(1), MaxValueValidator(65535)], ) rsync_mode = models.CharField( max_length=20, choices=choices.RSYNC_MODE_CHOICES, default='module', ) rsync_remotemodule = models.CharField( max_length=120, verbose_name=_("Remote Module Name"), blank=True, help_text=_("Name of the module defined in the remote rsync " "daemon"), ) rsync_remotepath = models.CharField( max_length=120, verbose_name=_("Remote Path"), blank=True, help_text=_("Path on remote host to rsync to, e.g. /mnt/tank"), ) rsync_direction = models.CharField( max_length=10, verbose_name=_("Direction"), help_text=_("Push - From local to remote machine. Pull - From " "remote to local machine."), default='push', choices=choices.RSYNC_DIRECTION, ) rsync_desc = models.CharField( max_length=120, verbose_name=_("Short description"), blank=True, ) rsync_minute = models.CharField( max_length=100, default="00", verbose_name=_("Minute"), help_text=_("Values 0-59 allowed."), ) rsync_hour = models.CharField( max_length=100, default="*", verbose_name=_("Hour"), help_text=_("Values 0-23 allowed."), ) rsync_daymonth = models.CharField( max_length=100, default="*", verbose_name=_("Day of month"), help_text=_("Values 1-31 allowed."), ) rsync_month = models.CharField( max_length=100, default='*', verbose_name=_("Month"), ) rsync_dayweek = models.CharField( max_length=100, default="*", verbose_name=_("Day of week"), ) rsync_user = UserField( max_length=60, verbose_name=_("User"), help_text=_("The user to run the command"), ) rsync_recursive = models.BooleanField( verbose_name=_("Recursive"), help_text=_("Recurse into directories"), default=True, ) rsync_times = models.BooleanField( verbose_name=_("Times"), help_text=_("Preserve modification times"), default=True, ) rsync_compress = models.BooleanField( verbose_name=_("Compress"), help_text=_("Compress data during the transfer"), default=True, ) rsync_archive = models.BooleanField( verbose_name=_("Archive"), help_text=_("Archive mode"), default=False, ) rsync_delete = models.BooleanField( verbose_name=_("Delete"), help_text=_("Delete files on the receiving side that don't exist " "on sender"), default=False, ) rsync_quiet = models.BooleanField( verbose_name=_("Quiet"), help_text=_("Suppress non-error messages"), default=False, ) rsync_preserveperm = models.BooleanField( verbose_name=_("Preserve permissions"), help_text=_("This option causes the receiving rsync to set the " "destination permissions to be the same as the source " "permissions"), default=False, ) rsync_preserveattr = models.BooleanField( verbose_name=_("Preserve extended attributes"), help_text=_("This option causes rsync to update the remote " "extended attributes to be the same as the local ones"), default=False, ) rsync_extra = models.TextField( verbose_name=_("Extra options"), help_text=_("Extra options to rsync command line (usually empty)"), blank=True) rsync_enabled = models.BooleanField( default=True, verbose_name=_("Enabled"), ) class Meta: verbose_name = _("Rsync Task") verbose_name_plural = _("Rsync Tasks") ordering = ["rsync_path", "rsync_desc"] def __unicode__(self): if self.rsync_desc: return self.rsync_desc elif self.rsync_mode == 'module': return self.rsync_remotemodule else: return self.rsync_remotepath def get_human_minute(self): if self.rsync_minute == '*': return _(u'Every minute') elif self.rsync_minute.startswith('*/'): return _(u'Every %s minute(s)') % self.rsync_minute.split('*/')[1] else: return self.rsync_minute def get_human_hour(self): if self.rsync_hour == '*': return _(u'Every hour') elif self.rsync_hour.startswith('*/'): return _(u'Every %s hour(s)') % self.rsync_hour.split('*/')[1] else: return self.rsync_hour def get_human_daymonth(self): if self.rsync_daymonth == '*': return _(u'Everyday') elif self.rsync_daymonth.startswith('*/'): return _(u'Every %s days') % self.rsync_daymonth.split('*/')[1] else: return self.rsync_daymonth def get_human_month(self): months = self.rsync_month.split(',') if len(months) == 12 or self.rsync_month == '*': return _("Every month") mchoices = dict(choices.MONTHS_CHOICES) labels = [] for m in months: if m in ('10', '11', '12'): m = chr(87 + int(m)) labels.append(unicode(mchoices[m])) return ', '.join(labels) def get_human_dayweek(self): weeks = self.rsync_dayweek.split(',') if len(weeks) == 7 or self.rsync_dayweek == '*': return _('Everyday') if weeks == map(str, xrange(1, 6)): return _('Weekdays') if weeks == map(str, xrange(6, 8)): return _('Weekends') wchoices = dict(choices.WEEKDAYS_CHOICES) labels = [] for w in weeks: labels.append(unicode(wchoices[str(w)])) return ', '.join(labels) def commandline(self): line = '/usr/bin/lockf -s -t 0 -k \'%s\' /usr/local/bin/rsync' % ( self.rsync_path) if self.rsync_recursive: line += ' -r' if self.rsync_times: line += ' -t' if self.rsync_compress: line += ' -z' if self.rsync_archive: line += ' -a' if self.rsync_preserveperm: line += ' -p' if self.rsync_preserveattr: line += ' -X' if self.rsync_delete: line += ' --delete-delay' line += ' --delay-updates %s' % self.rsync_extra # Do not use username if one is specified in host field # See #5096 for more details if '@' in self.rsync_remotehost: remote = self.rsync_remotehost else: remote = '%s@%s' % ( self.rsync_user, self.rsync_remotehost, ) if self.rsync_mode == 'module': if self.rsync_direction == 'push': line += ' \'%s\' %s::%s' % ( self.rsync_path, remote, self.rsync_remotemodule, ) else: line += ' %s::%s \'%s\'' % ( remote, self.rsync_remotemodule, self.rsync_path, ) else: line += (' -e \'ssh -p %d -o BatchMode=yes ' '-o StrictHostKeyChecking=yes\'') % ( self.rsync_remoteport) if self.rsync_direction == 'push': line += ' \'%s\' %s:\'%s\'' % ( self.rsync_path, remote, self.rsync_remotepath, ) else: line += ' %s:\'%s\' \'%s\'' % ( remote, self.rsync_remotepath, self.rsync_path, ) if self.rsync_quiet: line += ' > /dev/null 2>&1' return line def run(self): subprocess.Popen( 'su -m %s -c "%s" 2>&1 | logger -t rsync' % ( self.rsync_user, self.commandline(), ), shell=True, ) def delete(self): super(Rsync, self).delete() try: notifier().restart("cron") except: pass