Пример #1
0
class PluginsPlugin (SectionPlugin):
    def init(self):
        self.title = _('Plugins')
        self.icon = 'cogs'
        self.category = ''
        self.order = 60

        # In case you didn't notice it yet, this is the Plugins Plugin Plugin
        self.append(self.ui.inflate('plugins:main'))

        def post_plugin_bind(object, collection, item, ui):
            if not item.crash:
                ui.find('crash').visible = False

        def post_dep_bind(object, collection, item, ui):
            if not item.satisfied():
                installer = ui.find('fix')
                if item.__class__ == ModuleDependency:
                    installer.package = 'python-module-' + item.module_name
                if item.__class__ == BinaryDependency:
                    installer.package = item.binary_name
                installer.recheck()

        self.find('plugins').post_item_bind = post_plugin_bind
        self.find('dependencies').post_item_bind = post_dep_bind

        self.binder = Binder(None, self.find('bind-root'))

    def on_page_load(self):
        self.context.endpoint.send_progress(_('Gathering plugin list'))
        self.plugins = sorted(manager.get_all().values())
        self.binder.setup(self).populate()
Пример #2
0
class Netatalk (SectionPlugin):
    config_path = '/etc/afp.conf'

    def init(self):
        self.title = 'Netatalk'
        self.icon = 'folder-close'
        self.category = _('Software')
        self.append(self.ui.inflate('netatalk:main'))

        if not os.path.exists(self.config_path):
            open(self.config_path, 'w').write("[Global]")

        self.binder = Binder(None, self.find('config'))
        self.find('shares').new_item = lambda c: ShareData()
        self.config = NetatalkConfig(path=self.config_path)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
Пример #3
0
class Squid (SectionPlugin):
    def init(self):
        self.title = 'Squid'
        self.icon = 'exchange'
        self.category = _('Software')
        self.append(self.ui.inflate('squid:main'))

        self.find('servicebar').name = platform_select(
            debian='squid3',
            centos='squid',
            default='squid',
        )
        self.find('servicebar').reload()

        self.binder = Binder(None, self.find('config'))
        self.find('acl').new_item = lambda c: ACLData(name='new')
        self.find('http_access').new_item = lambda c: HTTPAccessData()
        self.find('http_port').new_item = lambda c: HTTPPortData()
        self.find('https_port').new_item = lambda c: HTTPSPortData()
        for e in self.nearest(lambda x: x.id == 'options'):
            e.new_item = lambda c: ArgumentData()
        self.config = SquidConfig(path='/etc/squid3/squid.conf')

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
Пример #4
0
class Exports (SectionPlugin):
    config_path = '/etc/exports'

    def init(self):
        self.title = _('NFS Exports')
        self.icon = 'hdd'
        self.category = _('Software')
        self.append(self.ui.inflate('exports:main'))

        if not os.path.exists(self.config_path):
            open(self.config_path, 'w').close()

        self.config = ExportsConfig(path=self.config_path)
        self.binder = Binder(None, self)
        self.find('exports').new_item = lambda c: ExportData()
        self.find('clients').new_item = lambda c: ClientData()

    def on_page_load(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.context.notify('info', _('Saved'))
Пример #5
0
class Supervisor (SectionPlugin):
    def init(self):
        self.title = 'Supervisor'
        self.icon = 'play'
        self.category = _('Software')
        self.append(self.ui.inflate('supervisor:main'))
        self.mgr = SupervisorServiceManager.get()
        self.binder = Binder(None, self.find('main'))
        self.find('programs').new_item = lambda c: ProgramData()
        self.config = SupervisorConfig(path=platform_select(
            default='/etc/supervisor/supervisord.conf',
            centos='/etc/supervisord.conf',
        ))
        self.find('servicebar').name = platform_select(
            centos='supervisord',
            default='supervisor',
        )
        self.find('servicebar').reload()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.mgr.fill(self.config.tree.programs)
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
Пример #6
0
class DHCPDPlugin (SectionPlugin):
    def init(self):
        self.title = _('DHCP Server')
        self.icon = 'sitemap'
        self.category = _('Software')

        self.append(self.ui.inflate('dhcpd:main'))

        self.config = DHCPDConfig(path='/etc/dhcp/dhcpd.conf')
        self.binder = Binder(None, self)

        for x in self.nearest(lambda x: x.bind == 'ranges'):
            x.new_item = lambda c: RangeData()
        for x in self.nearest(lambda x: x.bind == 'options'):
            x.new_item = lambda c: OptionData()
        self.find('subnets').new_item = lambda c: SubnetData()

    def on_page_load(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
Пример #7
0
class PureFTPDExtension (BaseExtension):
    default_config = {
        'created': False,
        'password': None,
    }
    name = 'FTP'

    def init(self):
        self.append(self.ui.inflate('vh-pureftpd:ext'))
        self.binder = Binder(self, self)

        if not 'username' in self.config:
            self.config['username'] = self.website.slug
            
        if not 'system_user' in self.config:
            self.config['system_user'] = ""

        if not 'system_group' in self.config:
            self.config['system_group'] = ""


        if not self.config['created']:
            self.config['password'] = str(uuid.uuid4())
            self.config['path'] = self.website.root
            self.config['created'] = True

        self.refresh()

    def refresh(self):
        self.binder.setup().populate()

    def update(self):
        self.binder.update()
Пример #8
0
class VSFTPDExtension (BaseExtension):
    default_config = {
        'created': False,
        'user': None,
        'password': None,
    }
    name = 'FTP'

    def init(self):
        self.append(self.ui.inflate('vh-vsftpd:ext'))
        self.binder = Binder(self, self)

        self.config['username'] = self.website.slug

        if not self.config['created']:
            self.config['password'] = str(uuid.uuid4())
            self.config['created'] = True

        self.refresh()

    def refresh(self):
        self.binder.setup().populate()

    def update(self):
        pass
Пример #9
0
class TaskManager (SectionPlugin):
    def init(self):
        self.title = _('Processes')
        self.icon = 'th-list'
        self.category = _('System')
        self.append(self.ui.inflate('taskmgr:main'))

        def post_item_bind(object, collection, item, ui):
            ui.find('term').on('click', self.on_term, item)
            ui.find('kill').on('click', self.on_kill, item)

        self.find('processes').post_item_bind = post_item_bind

        self.binder = Binder(None, self)
        self.sorting = '_cpu'
        self.sorting_reverse = True

        for x in ['_cpu', 'pid', '_sort_ram', '_sort_name']:
            self.find('sort-by-' + x).on('click', self.sort, x)

    def on_page_load(self):
        self.refresh()

    def sort(self, by):
        if self.sorting == by:
            self.sorting_reverse = not self.sorting_reverse
        else:
            self.sorting_reverse = by in ['_cpu', '_ram']
        self.sorting = by
        self.refresh()

    def refresh(self):
        self.processes = list(psutil.process_iter())
        for p in self.processes:
            try:
                p._name = p.name
                p._cmd = ' '.join(p.cmdline)
                p._cpu = p.get_cpu_percent(interval=0)
                p._ram = '%i K' % int(p.get_memory_info()[0] / 1024)
                p._ppid = p.ppid
                p._sort_ram = p.get_memory_info()[0]
                p._sort_name = p.name.lower()
                try:
                    p._username = p.username
                except:
                    p._username = '******'
            except psutil.NoSuchProcess:
                self.processes.remove(p)

        self.processes = sorted(self.processes, key=lambda x: getattr(x, self.sorting), reverse=self.sorting_reverse)
        self.binder.setup(self).populate()

    def on_term(self, p):
        os.kill(p.pid, 15)
        self.refresh()

    def on_kill(self, p):
        os.kill(p.pid, 9)
        self.refresh()
Пример #10
0
class NSDPlugin (SectionPlugin):
    def init(self):
        self.title = 'NSD'
        self.icon = 'globe'
        self.category = _('Software')

        self.append(self.ui.inflate('nsd:main'))

        self.config = NSDConfig(path='/etc/nsd3/nsd.conf')
        self.binder = Binder(None, self)
        self.find('zones').new_item = lambda c: ZoneData()

        def post_zone_bind(o, c, i, u):
            path = i.file
            if not path.startswith('/'):
                path = '/etc/nsd3/' + path
            exists = os.path.exists(path)
            u.find('no-file').visible = not exists
            u.find('file').visible = exists
            if exists:
                u.find('editor').value = open(path).read()

            def on_save_zone():
                open(path, 'w').write(u.find('editor').value)
                self.context.notify('info', _('Zone saved'))

            def on_create_zone():
                open(path, 'w').write("""$TTL    604800
@       IN      SOA     ns. root.ns. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@                   IN      NS      ns.
example.com.        IN      A       127.0.0.1
example.com.        IN      AAAA    ::1
""")
                post_zone_bind(o, c, i, u)

            u.find('save-zone').on('click', on_save_zone)
            u.find('create-zone').on('click', on_create_zone)
        self.find('zones').post_item_bind = post_zone_bind

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #11
0
class MySQLExtension (BaseExtension):
    default_config = {
        'created': False,
        'name': None,
        'user': None,
        'password': None,
    }
    name = 'MySQL'

    def init(self):
        self.append(self.ui.inflate('vh-mysql:ext'))
        self.binder = Binder(self, self)
        self.refresh()
        self.db = MySQLDB.get()

    @staticmethod
    def selftest():
        try:
            MySQLDB.get().query_databases()
        except:
            pass

    def refresh(self):
        self.binder.setup().populate()
        self.find('db-name').value = self.website.slug
        
    def update(self):
        self.binder.update()

    def on_destroy(self):
        if self.config['created']:
            self.on_delete()

    @on('create', 'click')
    def on_create(self):
        try:
            self.db.query_databases()
        except Exception, e:
            self.context.notify('error', str(e))
            self.context.launch('configure-plugin', plugin=self.db)
            return

        dbname = self.find('db-name').value
        for db in self.db.query_databases():
            if db.name == dbname:
                self.context.notify('error', 'This DB name is already used')
                return
        
        self.config['username'] = self.website.slug
        self.config['password'] = str(uuid.uuid4())
        self.config['name'] = dbname
        
        try:
            self.db.query_create(self.config['name'])
        except Exception, e:
            self.context.notify('error', str(e))
            return
Пример #12
0
class Filesystems (SectionPlugin):
    def init(self):
        self.title = _('Filesystems')
        self.icon = 'hdd'
        self.category = _('System')
        self.append(self.ui.inflate('fstab:main'))

        self.find('type').labels = ['Auto', 'EXT2', 'EXT3', 'EXT4', 'NTFS', 'FAT', 'ZFS', 'ReiserFS', 'Samba', 'None', 'Loop']
        self.find('type').values = ['auto', 'ext2', 'ext3', 'ext4', 'ntfs', 'vfat', 'zfs', 'reiser',  'smb',   'none', 'loop']

        self.fstab_config = FSTabConfig(path='/etc/fstab')
        self.mounts = MountsBackend.get()

        self.binder = Binder(None, self)
        self.find('fstab').find('filesystems').new_item = lambda c: FilesystemData()

        def post_mount_bind(object, collection, item, ui):
            ui.find('umount').on('click', self.on_umount, item)

        self.find('mounts').find('filesystems').post_item_bind = post_mount_bind

    def on_page_load(self):
        self.refresh()

    def on_umount(self, mount):
        subprocess.call(['umount', mount.mountpoint])
        self.context.notify('info', _('Unmounted'))
        self.refresh()

    @on('mount-all', 'click')
    def on_mount_all(self):
        self.save()
        if subprocess.call(['mount', '-a']):
            self.context.notify('error', _('mount -a failed'))
        self.refresh()

    @on('refresh', 'click')
    def refresh(self):
        self.binder.unpopulate()
        self.reload_disks()
        self.fstab_config.load()
        self.fstab = self.fstab_config.tree
        self.mounts.reload()
        self.binder.setup(self).populate()

    def reload_disks(self):
        lst = disks.list_devices(by_uuid=True, by_id=True, by_label=True)
        self.find('device').labels = [x[0] for x in lst]
        self.find('device').values = [x[1] for x in lst]

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.fstab_config.save()
        self.context.notify('info', _('Saved'))
Пример #13
0
class SNMPDPlugin (SectionPlugin):
    service_name = platform_select(
        default='snmpd',
    )

    def init(self):
        self.title = 'SNMP'
        self.icon = 'exchange'
        self.category = _('Software')

        self.append(self.ui.inflate('snmpd:main'))

        self.find('servicebar').name = self.service_name
        self.find('servicebar').reload()

        self.snmp_config = SNMPConfig(path=platform_select(
            default='/etc/snmp/snmp.conf',
        ))

        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.snmp_config.load()
        enabled_mibs = []
        for mib in self.snmp_config.tree.mibs:
            for x in mib.name.strip('-+:').split(':'):
                enabled_mibs.append(x)
        self.mibs = []
        for dirpath, dirname, filenames in os.walk('/usr/share/mibs', followlinks=True):
            for x in filenames:
                if not x.startswith('.'):
                    mib = MIBData()
                    mib.name = x
                    mib.selected = x in enabled_mibs
                    self.mibs.append(mib)
        self.mibs = sorted(self.mibs, key=lambda x: x.name)
        self.binder.setup(self).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        
        mib = MIBData()
        mib.name = ':'.join([x.name for x in self.mibs if x.selected])
        for x in list(self.snmp_config.tree.mibs):
            self.snmp_config.tree.mibs.remove(x)
        self.snmp_config.tree.mibs.append(mib)

        self.snmp_config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
        ServiceMultiplexor.get().get_one(self.service_name).restart()
Пример #14
0
class Cron(SectionPlugin):
    def init(self):
        self.title = "Cron"
        self.icon = "time"
        self.category = _("System")
        self.append(self.ui.inflate("cron:main"))

        def create_task(cls):
            logging.info("[cron] created a %s" % cls.__name__)
            return cls()

        def remove_task(i, c):
            c.remove(i)
            logging.info("[cron] removed %s" % getattr(i, "command", None))

        self.binder = Binder(None, self.find("config"))
        self.find("normal_tasks").new_item = lambda c: create_task(CrontabNormalTaskData)
        self.find("special_tasks").new_item = lambda c: create_task(CrontabSpecialTaskData)
        self.find("env_settings").new_item = lambda c: create_task(CrontabEnvSettingData)
        self.find("normal_tasks").delete_item = remove_task
        self.find("special_tasks").delete_item = remove_task
        self.find("env_settings").delete_item = remove_task

        self.current_user = "******"

    def on_page_load(self):
        self.refresh()

    @on("user-select", "click")
    def on_user_select(self):
        self.current_user = self.find("users").value
        logging.info("[cron] selected user %s" % self.current_user)
        self.refresh()

    def refresh(self):
        users_select = self.find("users")
        users_select.value = self.current_user
        users = [
            x.name for x in PasswdConfig(path="/etc/passwd").load().tree.users if int(x.uid) >= 500 or x.name == "root"
        ]
        users_select.values = users_select.labels = users

        self.config = CronManager.get().load_tab(self.current_user)
        self.binder.setup(self.config.tree).populate()

    @on("save", "click")
    def on_save(self):
        self.binder.update()
        logging.info("[cron] edited tasks")
        try:
            CronManager.get().save_tab(self.current_user, self.config)
            self.refresh()
        except Exception, e:
            self.context.notify("error", e.message)
Пример #15
0
class NTPDPlugin(SectionPlugin):
    service_name = platform_select(default="ntp", centos="ntpd")

    def init(self):
        self.title = _("Date & Time")
        self.icon = "time"
        self.category = _("Software")

        self.append(self.ui.inflate("ntpd:main"))

        self.find("servicebar").name = self.service_name
        self.find("servicebar").reload()

        self.config = NTPDConfig(path=platform_select(default="/etc/ntp.conf"))

        self.binder = Binder(None, self)

        self.find("servers").new_item = lambda c: ServerData()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.now = int(time.time())
        self.binder.setup(self).populate()

    @on("set", "click")
    def on_set(self):
        self.binder.update()
        d = datetime.fromtimestamp(self.now)
        s = d.strftime("%m%d%H%M%Y")
        subprocess.call(["date", s])
        self.refresh()

    @on("sync", "click")
    def on_sync(self):
        self.binder.update()
        if len(self.config.tree.servers) == 0:
            self.context.notify("error", _("No servers defined"))
            return
        server = self.config.tree.servers[0].address
        output = subprocess.check_output(["ntpdate", "-u", server])
        self.context.notify("info", _("Done"))
        self.context.notify("info", output)
        self.refresh()

    @on("save", "click")
    def save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
        self.context.notify("info", _("Saved"))
        ServiceMultiplexor.get().get_one(self.service_name).restart()
Пример #16
0
class Cron (SectionPlugin):
    def init(self):
        self.title = 'Cron'
        self.icon = 'time'
        self.category = _('System')
        self.append(self.ui.inflate('cron:main'))

        def create_task(cls):
            logging.info('[cron] created a %s' % cls.__name__)
            return cls()

        def remove_task(i, c):
            c.remove(i)
            logging.info('[cron] removed %s' % getattr(i, 'command', None))

        self.binder = Binder(None, self.find('config'))
        self.find('normal_tasks').new_item = lambda c: create_task(CrontabNormalTaskData)
        self.find('special_tasks').new_item = lambda c: create_task(CrontabSpecialTaskData)
        self.find('env_settings').new_item = lambda c: create_task(CrontabEnvSettingData)
        self.find('normal_tasks').delete_item = remove_task
        self.find('special_tasks').delete_item = remove_task
        self.find('env_settings').delete_item = remove_task

        self.current_user = '******'

    def on_page_load(self):
        self.refresh()

    @on('user-select', 'click')
    def on_user_select(self):
        self.current_user = self.find('users').value
        logging.info('[cron] selected user %s' % self.current_user)
        self.refresh()

    def refresh(self):
        users_select = self.find('users')
        users_select.value = self.current_user
        users = [x.name for x in PasswdConfig(path='/etc/passwd').load().tree.users]
        users_select.values = users_select.labels = users

        self.config = CronManager.get().load_tab(self.current_user)
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        logging.info('[cron] edited tasks')
        try:
            CronManager.get().save_tab(self.current_user, self.config)
            self.refresh()
        except Exception as e:
            self.context.notify('error', e.message)
Пример #17
0
class LinuxIfUpDownNetworkConfigSet (NetworkConfigBit):
    cls = 'linux-ifupdown'
    title = 'Scripts'

    def init(self):
        self.append(self.ui.inflate('network:bit-linux-ifupdown'))
        self.binder = Binder(None, self)

    def refresh(self):
        self.binder.setup(self.iface).populate()

    def apply(self):
        self.binder.update()
Пример #18
0
class LinuxDHCPNetworkConfigSet (NetworkConfigBit):
    cls = 'linux-dhcp'
    title = 'DHCP'

    def init(self):
        self.append(self.ui.inflate('network:bit-linux-dhcp'))
        self.binder = Binder(None, self)

    def refresh(self):
        self.binder.setup(self.iface).populate()

    def apply(self):
        self.binder.update()
Пример #19
0
class Test (SectionPlugin):
    def init(self):
        self.title = 'Test'
        self.icon = 'question'
        self.category = 'Demo'

        self.append(self.ui.inflate('test:main'))

        alice = Person('Alice', [Phone('123')])
        bob = Person('Bob', [Phone('234'), Phone('345')])
        book = AddressBook([alice, bob])

        self.find('persons-collection').new_item = lambda c: Person('New person', [])
        self.find('phones-collection').new_item = lambda c: Phone('123')

        self.binder = Binder(None, self.find('addressbook'))
        self.binder.setup(book).populate()
Пример #20
0
class RAID (SectionPlugin):
    def init(self):
        self.title = 'LSI MegaRAID'
        self.icon = 'hdd'
        self.category = _('System')

        self.append(self.ui.inflate('megaraid:main'))

        self.mgr = RAIDManager.get()
        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.mgr.refresh()
        self.binder.setup(self.mgr).populate()
Пример #21
0
class Munin (SectionPlugin):
    def init(self):
        self.title = 'Munin'
        self.icon = 'stethoscope'
        self.category = _('Software')
        self.append(self.ui.inflate('munin:main'))

        def post_graph_bind(o, c, i, u):
            for plot in u.nearest(lambda x: x.typeid == 'munin:plot'):
                plot.on('widget', self.on_add_widget, i)

        self.find('graphs').post_item_bind = post_graph_bind

        self.munin_client = MuninClient.get()
        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def on_add_widget(self, graph, url=None, period=None):
        self.context.launch('dashboard-add-widget', cls=MuninWidget, config={'url': url, 'period': period})

    def refresh(self):
        self.munin_client.reset()
        try:
            self.munin_client.fetch_domains()
        except requests.ConnectionError as e:
            self.find_type('tabs').active = 1
            self.context.notify('error', _('Couldn\'t connect to Munin: %s') % e.message)
        except Exception as e:
            self.find_type('tabs').active = 1
            if e.message == 'auth':
                self.context.notify('error', _('Munin HTTP authentication failed'))
            else:
                raise
        self.binder.setup(self.munin_client).populate()

    @on('save-button', 'click')
    def save(self):
        self.binder.update()
        self.munin_client.save_classconfig()
        self.refresh()
        self.find_type('tabs').active = 0
Пример #22
0
class ProcessesExtension (BaseExtension):
    default_config = {
        'processes': [],
    }
    name = _('Processes')

    def init(self):
        self.append(self.ui.inflate('vh:ext-processes'))
        self.binder = Binder(self, self)
        self.find('processes').new_item = lambda c: WebsiteProcess()
        self.refresh()

    def refresh(self):
        self.processes = [WebsiteProcess(x) for x in self.config['processes']]
        self.binder.setup().populate()

    def update(self):
        self.binder.update()
        self.config['processes'] = [x.save() for x in self.processes]
Пример #23
0
class Hosts (SectionPlugin):
    def init(self):
        self.title = _('Hosts')
        self.icon = 'sitemap'
        self.category = _('System')

        self.append(self.ui.inflate('hosts:main'))

        self.config = HostsConfig(path='/etc/hosts')
        self.binder = Binder(None, self.find('hosts-config'))
        self.find('aliases').new_item = lambda c: AliasData()
        self.find('hosts').new_item = lambda c: HostData()

    def on_page_load(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
Пример #24
0
class Services (SectionPlugin):
    def init(self):
        self.title = _('Services')
        self.icon = 'play'
        self.category = _('Software')
        self.append(self.ui.inflate('services:main'))
        self.mgr = ServiceMultiplexor.get()
        self.binder = Binder(None, self.find('main'))

        def post_item_bind(object, collection, item, ui):
            ui.find('stop').on('click', self.on_stop, item)
            ui.find('restart').on('click', self.on_restart, item)
            ui.find('start').on('click', self.on_start, item)
            ui.find('stop').visible = item.running
            ui.find('restart').visible = item.running
            ui.find('start').visible = not item.running

        self.find('services').post_item_bind = post_item_bind

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.services = sorted(self.mgr.get_all(), key=lambda x: x.name)
        self.binder.setup(self).populate()

    def on_start(self, item):
        item.start()
        self.refresh()
        self.context.notify('info', _('%s started') % item.name)

    def on_stop(self, item):
        item.stop()
        self.refresh()
        self.context.notify('info', _('%s stopped') % item.name)

    def on_restart(self, item):
        item.restart()
        self.refresh()
        self.context.notify('info', _('%s restarted') % item.name)
Пример #25
0
class PureFTPDExtension(BaseExtension):
    default_config = {"created": False, "password": None}
    name = "FTP"

    def init(self):
        self.append(self.ui.inflate("vh-pureftpd:ext"))
        self.binder = Binder(self, self)

        if not "username" in self.config:
            self.config["username"] = self.website.slug

        if not self.config["created"]:
            self.config["password"] = str(uuid.uuid4())
            self.config["created"] = True

        self.refresh()

    def refresh(self):
        self.binder.setup().populate()

    def update(self):
        self.binder.update()
Пример #26
0
class RAID (SectionPlugin):
    def init(self):
        self.title = _('RAID')
        self.icon = 'hdd'
        self.category = _('System')

        self.append(self.ui.inflate('raid:main'))

        def post_array_bind(o, c, i, u):
            u.find('recovery').visible = i.recovery

        self.find('arrays').post_item_bind = post_array_bind

        self.mgr = RAIDManager.get()
        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.mgr.refresh()
        self.binder.setup(self.mgr).populate()
Пример #27
0
class Cron (SectionPlugin):
    def init(self):
        self.title = 'Cron'
        self.icon = 'time'
        self.category = _('System')
        self.append(self.ui.inflate('cron:main'))

        self.binder = Binder(None, self.find('config'))
        self.find('normal_tasks').new_item = lambda c: CrontabNormalTaskData()
        self.find('special_tasks').new_item = lambda c: CrontabSpecialTaskData()
        self.find('env_settings').new_item = lambda c: CrontabEnvSettingData()

        self.current_user = '******'

    def on_page_load(self):
        self.refresh()

    @on('user-select', 'click')
    def on_user_select(self):
        self.current_user = self.find('users').value
        self.refresh()

    def refresh(self):
        users_select = self.find('users')
        users_select.value = self.current_user
        users = [x.name for x in PasswdConfig(path='/etc/passwd').load().tree.users if int(x.uid) >= 500 or x.name == 'root']
        users_select.values = users_select.labels = users

        self.config = CronManager.get().load_tab(self.current_user)
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        try:
            CronManager.get().save_tab(self.current_user, self.config)
            self.refresh()
        except Exception, e:
            self.context.notify('error', e.message)
Пример #28
0
class Resolv (SectionPlugin):
    def init(self):
        self.title = _('Nameservers')
        self.icon = 'globe'
        self.category = _('System')

        self.append(self.ui.inflate('resolv:main'))
        self.find('name-box').labels = [_('DNS nameserver'), _('Local domain name'), _('Search list'), _('Sort list'), _('Options')]
        self.find('name-box').values = ['nameserver', 'domain', 'search', 'sortlist', 'options']

        self.config = ResolvConfig(path='/etc/resolv.conf')
        self.binder = Binder(None, self.find('resolv-config'))
        self.find('items').new_item = lambda c: ItemData()

    def on_page_load(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
Пример #29
0
class CTDB(SectionPlugin):
    nodes_file = "/etc/ctdb/nodes"
    addresses_file = "/etc/ctdb/public_addresses"

    def init(self):
        self.title = _("Samba Cluster")
        self.icon = "folder-close"
        self.category = _("Software")

        self.append(self.ui.inflate("ctdb:main"))

        self.config_path = {"debian": "/etc/default/ctdb", "centos": "/etc/sysconfig/ctdb"}[ajenti.platform]

        self.config = CTDBConfig(path=self.config_path)
        self.config.load()

        self.binder = Binder(None, self.find("main-config"))
        self.n_binder = Binder(None, self.find("nodes-config"))
        self.a_binder = Binder(None, self.find("addresses-config"))
        self.find("nodes").new_item = lambda c: NodeData()
        self.find("addresses").new_item = lambda c: PublicAddressData()

    def on_page_load(self):
        n_path = self.config.tree.nodes_file
        self.nodes_config = CTDBNodesConfig(path=n_path)
        if not os.path.exists(n_path):
            open(n_path, "w").close()
        self.nodes_config.load()

        a_path = self.config.tree.public_addresses_file
        self.addresses_config = CTDBPublicAddressesConfig(path=a_path)
        if not os.path.exists(a_path):
            open(a_path, "w").close()
        self.addresses_config.load()

        self.config.load()
        self.binder.setup(self.config.tree).populate()
        self.n_binder.setup(self.nodes_config.tree).populate()
        self.a_binder.setup(self.addresses_config.tree).populate()
        self.refresh()

    @on("refresh", "click")
    def refresh(self):
        try:
            self.find("status").value = subprocess.check_output(["ctdb", "status"])
            self.find("status-ip").value = subprocess.check_output(["ctdb", "ip"])
        except:
            self.find("status").value = _("Failed to obtain CTDB status")

    @on("save", "click")
    def save(self):
        self.binder.update()
        self.n_binder.update()
        self.a_binder.update()
        self.config.save()
        self.nodes_config.save()
        self.addresses_config.save()
        self.context.notify("info", _("Saved"))
Пример #30
0
class Samba(SectionPlugin):
    def init(self):
        self.title = 'Samba'
        self.icon = 'folder-close'
        self.category = _('Software')
        self.append(self.ui.inflate('samba:main'))

        self.find('servicebar').name = platform_select(
            debian='samba',
            ubuntu='smbd',
            mageia='smbd',
            centos='smb',
            default='samba',
        )
        self.find('servicebar').reload()

        self.binder = Binder(None, self.find('config'))
        self.find('shares').new_item = lambda c: ShareData()
        self.config = SambaConfig(path=platform_select(
            default='/etc/samba/smb.conf',
            freebsd='/usr/local/etc/smb.conf',
        ))

        def post_item_bind(object, collection, item, ui):
            ui.find('disconnect').on('click', self.on_disconnect, item)

        self.find('connections').post_item_bind = post_item_bind

        def post_user_bind(object, collection, item, ui):
            def delete_user():
                self.usermgr.delete(item.username)
                self.refresh()

            ui.find('delete').on('click', delete_user)

            def set_password():
                if self.usermgr.set_password(item.username,
                                             ui.find('password').value):
                    self.context.notify('info', _('Password updated'))
                    ui.find('password').value = ''
                else:
                    self.context.notify('error', _('Password update failed'))

            ui.find('password-set').on('click', set_password)

        self.find('user-list').post_item_bind = post_user_bind

        self.usermgr = SambaUsers()
        self.binder_u = Binder(self.usermgr, self.find('users'))

        self.monitor = SambaMonitor()
        self.binder_m = Binder(self.monitor, self.find('status'))

    def on_page_load(self):
        self.refresh()

    def on_disconnect(self, connection):
        connection.disconnect()
        self.refresh()

    def refresh(self):
        self.config.load()
        self.monitor.refresh()
        self.usermgr.load()
        self.binder.setup(self.config.tree).populate()
        self.binder_m.setup(self.monitor).populate()
        self.binder_u.setup(self.usermgr).populate()

        users_dropdown = self.find('add-user-list')
        users = [
            x.name for x in PasswdConfig(path='/etc/passwd').load().tree.users
        ]
        for u in self.usermgr.users:
            if u.username in users:
                users.remove(u.username)
        users_dropdown.values = users_dropdown.labels = users

    @on('add-user', 'click')
    def on_add_user(self):
        self.usermgr.create(self.find('add-user-list').value)
        self.refresh()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
Пример #31
0
class NSDPlugin(SectionPlugin):
    def init(self):
        self.title = 'NSD'
        self.icon = 'globe'
        self.category = _('Software')

        self.append(self.ui.inflate('nsd:main'))

        self.config = NSDConfig(path='/etc/nsd3/nsd.conf')

        self.binder = Binder(None, self)
        self.find('zones').new_item = lambda c: ZoneData()

        def post_zone_bind(o, c, i, u):
            path = i.file
            if not path.startswith('/'):
                path = '/etc/nsd3/' + path
            exists = os.path.exists(path)
            u.find('no-file').visible = not exists
            u.find('file').visible = exists
            if exists:
                u.find('editor').value = open(path).read()

            def on_save_zone():
                open(path, 'w').write(u.find('editor').value)
                self.context.notify('info', _('Zone saved'))

            def on_create_zone():
                open(path, 'w').write("""$TTL    604800
@       IN      SOA     ns. root.ns. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@                   IN      NS      ns.
example.com.        IN      A       127.0.0.1
example.com.        IN      AAAA    ::1
""")
                post_zone_bind(o, c, i, u)

            u.find('save-zone').on('click', on_save_zone)
            u.find('create-zone').on('click', on_create_zone)

        self.find('zones').post_item_bind = post_zone_bind

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        if not os.path.exists(self.config.origin):
            self.context.notify('error',
                                _('%s does not exist') % self.config.origin)
            return
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #32
0
class CTDB(SectionPlugin):
    nodes_file = '/etc/ctdb/nodes'
    addresses_file = '/etc/ctdb/public_addresses'

    def init(self):
        self.title = _('Samba Cluster')
        self.icon = 'folder-close'
        self.category = _('Software')

        self.append(self.ui.inflate('ctdb:main'))

        self.config_path = {
            'debian': '/etc/default/ctdb',
            'centos': '/etc/sysconfig/ctdb'
        }[ajenti.platform]

        self.config = CTDBConfig(path=self.config_path)
        self.config.load()

        self.binder = Binder(None, self.find('main-config'))
        self.n_binder = Binder(None, self.find('nodes-config'))
        self.a_binder = Binder(None, self.find('addresses-config'))
        self.find('nodes').new_item = lambda c: NodeData()
        self.find('addresses').new_item = lambda c: PublicAddressData()

    def on_page_load(self):
        n_path = self.config.tree.nodes_file
        self.nodes_config = CTDBNodesConfig(path=n_path)
        if not os.path.exists(n_path):
            open(n_path, 'w').close()
        self.nodes_config.load()

        a_path = self.config.tree.public_addresses_file
        self.addresses_config = CTDBPublicAddressesConfig(path=a_path)
        if not os.path.exists(a_path):
            open(a_path, 'w').close()
        self.addresses_config.load()

        self.config.load()
        self.binder.setup(self.config.tree).populate()
        self.n_binder.setup(self.nodes_config.tree).populate()
        self.a_binder.setup(self.addresses_config.tree).populate()
        self.refresh()

    @on('refresh', 'click')
    def refresh(self):
        try:
            self.find('status').value = subprocess.check_output(
                ['ctdb', 'status'])
            self.find('status-ip').value = subprocess.check_output(
                ['ctdb', 'ip'])
        except:
            self.find('status').value = _('Failed to obtain CTDB status')

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.n_binder.update()
        self.a_binder.update()
        self.config.save()
        self.nodes_config.save()
        self.addresses_config.save()
        self.context.notify('info', _('Saved'))
Пример #33
0
class NetworkPlugin (SectionPlugin):
    platforms = ['debian', 'centos', 'mageia']

    def init(self):
        self.title = _('Network')
        self.icon = 'globe'
        self.category = _('System')
        self.net_config = INetworkConfig.get()

        self.append(self.ui.inflate('network:main'))

        def post_interface_bind(o, c, i, u):
            i.add_bits(self.ui)
            for bit in i.bits:
                u.find('bits').append(self.ui.create(
                    'tab',
                    children=[bit],
                    title=bit.title,
                ))
            u.find('up').on('click', self.on_up, i)
            u.find('down').on('click', self.on_down, i)
            u.find('restart').on('click', self.on_restart, i)
            u.find('ip').text = self.net_config.get_ip(i)

        def post_interface_update(o, c, i, u):
            for bit in i.bits:
                bit.apply()

        self.find('interfaces').post_item_bind = post_interface_bind
        self.find('interfaces').post_item_update = post_interface_update

        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.net_config.rescan()

        sensor = Sensor.find('traffic')
        for i in self.net_config.interface_list:
            i.tx, i.rx = sensor.value(i.name)

        self.binder.setup(self.net_config).populate()
        return

    def on_up(self, iface=None):
        self.save()
        self.net_config.up(iface)
        self.refresh()

    def on_down(self, iface=None):
        self.save()
        self.net_config.down(iface)
        self.refresh()

    def on_restart(self, iface=None):
        self.save()
        self.on_down(iface)
        self.on_up(iface)

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.net_config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #34
0
class WebsitesWebsiteEditorPlugin(SectionPlugin):
    uses_access_permission_of = WebsitesPlugin

    def init(self):
        self.title = 'Website editor'
        self.icon = 'globe'
        self.category = 'Web'
        self.order = 2
        self.hidden = True

        self.manager = VHManager.get()
        self.binder = Binder(None, self)

        self.append(self.ui.inflate('vh:main-website'))
        self.find(
            'domains').new_item = lambda c: WebsiteDomain.create('example.com')
        self.find('ports').new_item = lambda c: WebsitePort.create(80)

        def post_location_bind(object, collection, item, ui):
            ui.find('backend-params').empty()
            ui.find('backend-params').append(
                self.ui.inflate('vh:main-backend-params-%s' %
                                item.backend.type))
            item.backend.__binder = Binder(item.backend,
                                           ui.find('backend-params'))
            item.backend.__binder.populate()

        def post_location_update(object, collection, item, ui):
            item.backend.__binder.update()

        self.find('locations').post_item_bind = post_location_bind
        self.find('locations').post_item_update = post_location_update

        self.find('create-location-type').labels = []
        self.find('create-location-type').values = []
        for g in sorted(ApplicationGatewayComponent.get_classes(),
                        key=lambda x: x.title):
            self.find('create-location-type').labels.append(g.title)
            self.find('create-location-type').values.append(g.id)

    @intent('v:manage-website')
    def on_launch(self, website=None):
        self.activate()
        self.website = website
        self.binder.setup(self.website)
        self.binder.populate()

        for ext in BaseExtension.get_classes():
            ext.selftest()

        extensions = BaseExtension.get_classes()

        def create_location():
            self.binder.update()
            t = self.find('create-location-type').value
            l = WebsiteLocation.create(self.website, template=t)
            l.backend.type = t
            self.website.locations.append(l)
            self.refresh()

        self.find('create-location').on('click', create_location)

        # Extensions
        for tab in list(self.find('tabs').children):
            if hasattr(tab, '-is-extension'):
                tab.delete()

        self.website.extensions = []
        for ext in extensions:
            ext = ext.new(self.ui,
                          self.website,
                          config=self.website.extension_configs.get(
                              ext.classname, None))
            ext.editor_ui = self
            ext._ui_container = self.ui.create('tab',
                                               children=[ext],
                                               title=ext.name)
            setattr(ext._ui_container, '-is-extension', True)
            self.website.extensions.append(ext)
            self.find('tabs').append(ext._ui_container)

        # Root creator
        self.find('root-not-created').visible = not os.path.exists(
            self.website.root)

        def create_root():
            self.binder.update()
            if not os.path.exists(self.website.root):
                os.makedirs(self.website.root)
            subprocess.call(['chown', '-R', 'www-data:', self.website.root])
            self.save()

        self.find('create-root-directory').on('click', create_root)
        self.find('fix-root-permissions').on('click', create_root)
        self.find('set-path').on('click', self.save)

        # Downloader

        def download():
            url = self.find('download-url').value
            self.save()
            tmppath = '/tmp/ajenti-v-download'
            script = 'wget "%s" -O "%s" ' % (url, tmppath)
            if url.lower().endswith(
                ('tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2')):
                script += '&& tar xf "%s" -C "%s"' % (tmppath,
                                                      self.website.root)
            elif url.lower().endswith('.rar'):
                script += '&& unrar x "%s" "%s"' % (tmppath, self.website.root)
            elif url.lower().endswith('.zip'):
                script += '&& unzip "%s" -d "%s"' % (tmppath,
                                                     self.website.root)

            script += ' && chown www-data: -R "%s"' % self.website.root
            script += ' && find "%s" -type d -exec chmod 775 {} ";"' % self.website.root
            script += ' && find "%s" -type f -exec chmod 644 {} ";"' % self.website.root

            def callback():
                self.save()
                self.activate()
                if os.path.exists(tmppath):
                    os.unlink(tmppath)
                self.context.notify('info', _('Download complete'))

            self.context.launch('terminal', command=script, callback=callback)

        self.find('download').on('click', download)

    @on('go-back', 'click')
    def on_go_back(self):
        WebsitesPlugin.get().activate()

    @on('destroy', 'click')
    def on_destroy(self):
        for ext in self.website.extensions:
            try:
                ext.on_destroy()
            except Exception, e:
                logging.error(str(e))
        self.manager.config.websites.remove(self.website)
        self.save()
        self.on_go_back()
Пример #35
0
class ScriptInstaller(BaseExtension):
    default_config = {
        'databases': [],
        'users': [],
    }
    name = 'Script Installer'

    def init(self):
        self.append(self.ui.inflate('vh-script-installer:main'))
        self.binder = Binder(self, self)
        self.refresh()
        self.db = MySQLDB.get()

    @staticmethod
    def selftest():
        try:
            MySQLDB.get().query_databases()
        except:
            pass

    def refresh(self):
        if not 'databases' in self.config:
            if self.config['created']:
                self.config['databases'] = [{
                    'name': self.config['name'],
                }]
                self.config['users'] = [{
                    'name': self.config['user'],
                    'password': self.config['password'],
                }]
            else:
                self.config['databases'] = []
                self.config['users'] = []

            del self.config['created']
            del self.config['name']
            del self.config['user']
            del self.config['password']

        self.binder.setup().populate()
        self.find('wp-path').value = self.website.root
        self.find('wp-db-name').value = self.website.slug + '_wp'
        self.find('wp-db-user').value = self.website.slug + '_wp'

        self.find('wp-db-pass').value = str(uuid.uuid4())

        self.find('wp-db-name').delete_item = lambda i, c: self.on_delete_db(i)
        self.find(
            'wp-db-user').delete_item = lambda i, c: self.on_delete_user(i)

    def update(self):
        self.binder.update()

    def on_destroy(self):
        pass

    @on('wp-install', 'click')
    def on_install_wp(self):

        # Define User, Pass and DB Name
        db_user = self.find('wp-db-user').value
        db_pass = self.find('wp-db-pass').value
        db_name = self.find('wp-db-name').value

        # Download Latest Wordpress
        url = 'http://wordpress.org/latest.tar.gz'
        tmpfile = '/tmp/wp.tar.gz'

        with open(tmpfile, 'wb') as f:
            f.write(
                urllib2.urlopen('http://wordpress.org/latest.tar.gz').read())
            f.close()

        with closing(tarfile.open('/tmp/wp.tar.gz', 'r')) as tar:
            tar.extractall(path="/tmp/wp")

        for f in os.listdir('/tmp/wp/wordpress/'):
            src = '/tmp/wp/wordpress/' + f
            dst = self.website.root + '/' + f
            shutil.move(src, dst)

        if os.path.isfile(tmpfile):
            os.remove(tmpfile)

        if os.path.exists("/tmp/wp"):
            shutil.rmtree("/tmp/wp")

        self.context.notify('info', _('Wordpress Downloaded !'))

        # Generate Databases
        self.generate_db(db_name, db_user, db_pass)

        # Configure Wordpress
        config_string = ""
        if os.path.isfile(self.website.root + '/wp-config-sample.php'):

            if not os.path.isfile(self.website.root + '/wp-config.php'):

                salt = urllib2.urlopen(
                    'https://api.wordpress.org/secret-key/1.1/salt/').read()

                with open(self.website.root + '/wp-config-sample.php',
                          'r') as sample_file:
                    config_string = sample_file.read()
                    config_string = config_string.replace(
                        """define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');""", salt)
                    config_string = config_string.replace(
                        'database_name_here', db_name)
                    config_string = config_string.replace(
                        'username_here', db_user)
                    config_string = config_string.replace(
                        'password_here', db_pass)
                    sample_file.close()

                with open(self.website.root + '/wp-config.php',
                          'wb') as wp_config:
                    wp_config.write(config_string)
                    wp_config.close()

            else:
                self.context.notify('error', _('Config file already exists!'))
                return

        else:
            self.context.notify('error',
                                _('File wp-config-sample.php\nNot Found!'))
            return

        self.context.notify('info', _('Wordpress Installed !'))
        self.refresh()
        self.try_save()

    def generate_db(self, db_name, db_user, db_pass):

        # Create Database
        try:
            self.db.query_databases()
        except Exception, e:
            self.context.notify('error', str(e))
            self.context.launch('configure-plugin', plugin=self.db)
            return

        dbname = db_name

        for db in self.db.query_databases():
            if db.name == dbname:
                self.context.notify('error',
                                    _('This database name is already used'))
                return

        db_cfg = {'name': dbname}

        try:
            self.db.query_create(db_cfg['name'])
        except Exception, e:
            self.context.notify('error', str(e))
            return
Пример #36
0
class TaskManager(SectionPlugin):
    def init(self):
        self.title = _('Processes')
        self.icon = 'th-list'
        self.category = _('System')
        self.append(self.ui.inflate('taskmgr:main'))

        def post_item_bind(object, collection, item, ui):
            ui.find('term').on('click', self.on_term, item)
            ui.find('kill').on('click', self.on_kill, item)

        self.find('processes').post_item_bind = post_item_bind

        self.binder = Binder(None, self)
        self.sorting = '_cpu'
        self.sorting_reverse = True

        for x in ['_cpu', 'pid', '_sort_ram', '_sort_name']:
            self.find('sort-by-' + x).on('click', self.sort, x)

    def on_page_load(self):
        self.refresh()

    def sort(self, by):
        if self.sorting == by:
            self.sorting_reverse = not self.sorting_reverse
        else:
            self.sorting_reverse = by in ['_cpu', '_ram']
        self.sorting = by
        self.refresh()

    def refresh(self):
        self.processes = list(psutil.process_iter())
        for p in self.processes:
            try:
                p._name = get(p.name)
                p._cmd = ' '.join(get(p.cmdline))
                p._cpu = p.get_cpu_percent(interval=0)
                p._ram = '%i K' % int(p.get_memory_info()[0] / 1024)
                p._ppid = get(p.ppid)
                p._sort_ram = p.get_memory_info()[0]
                p._sort_name = get(p.name).lower()
                try:
                    p._username = get(p.username)
                except:
                    p._username = '******'
            except psutil.NoSuchProcess:
                self.processes.remove(p)

        self.processes = sorted(self.processes,
                                key=lambda x: getattr(x, self.sorting, None),
                                reverse=self.sorting_reverse)
        self.binder.setup(self).populate()

    def on_term(self, p):
        os.kill(p.pid, 15)
        self.refresh()

    def on_kill(self, p):
        os.kill(p.pid, 9)
        self.refresh()
Пример #37
0
class MailPlugin (SectionPlugin):
    def init(self):
        self.title = _('Mail')
        self.icon = 'envelope'
        self.category = 'Web'

        self.manager = MailManager.get()

        if not self.manager.is_configured:
            self.append(self.ui.inflate('vh-mail:not-configured'))
        else:
            self.post_init()

    @on('initial-enable', 'click')
    def on_initial_enable(self):
        self.post_init()
        self.manager.save()
        self.refresh()

    def post_init(self):
        self.empty()
        self.append(self.ui.inflate('vh-mail:main'))

        self.binder = Binder(None, self)

        def post_mb_bind(object, collection, item, ui):
            ui.find('size').text = str_fsize(self.manager.get_usage(item))

        def post_mb_update(object, collection, item, ui):
            if ui.find('password').value:
                item.password = ui.find('password').value

        self.find('mailboxes').post_item_bind = post_mb_bind
        self.find('mailboxes').post_item_update = post_mb_update
        self.find('mailboxes').filter = \
            lambda mb: self.context.session.identity in ['root', mb.owner]
        self.find('targets').new_item = lambda c: ForwardingTarget.create()

        self.binder.setup(self.manager.config)

    def _fetch_new_mailbox_name(self, cls):
        mb = cls.create()
        mb.local = self.find('new-mailbox-local').value
        mb.domain = self.find('new-mailbox-domain').value or \
            self.find('new-mailbox-domain-custom').value

        if not mb.local:
            self.context.notify('error', _('Invalid mailbox name'))
            return

        if not mb.domain:
            self.context.notify('error', _('Invalid mailbox domain'))
            return

        if cls == ForwardingMailbox:
            for existing in self.manager.config.forwarding_mailboxes:
                if existing.name == mb.name:
                    self.context.notify(
                        'error',
                        _('This address is already taken')
                    )
                    return
        else:
            for existing in self.manager.config.mailboxes:
                if existing.name == mb.name:
                    self.context.notify(
                        'error',
                        _('This address is already taken')
                    )
                    return

        self.find('new-mailbox-local').value = ''
        return mb

    @on('new-mailbox', 'click')
    def on_new_mailbox(self):
        self.binder.update()
        mb = self._fetch_new_mailbox_name(Mailbox)
        if not mb:
            return
        mb.owner = self.context.session.identity
        mb.password = ''
        self.manager.config.mailboxes.append(mb)
        self.manager.save()
        self.binder.populate()

    @on('new-forwarding-mailbox', 'click')
    def on_new_forwarding_mailbox(self):
        self.binder.update()
        mb = self._fetch_new_mailbox_name(ForwardingMailbox)
        if not mb:
            return
        mb.owner = self.context.session.identity
        self.manager.config.forwarding_mailboxes.append(mb)
        self.manager.save()
        self.binder.populate()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        if not self.manager.is_configured:
            return

        domains = []
        for ws in VHManager.get().config.websites:
            if self.context.session.identity in ['root', ws.owner]:
                domains += [d.domain for d in ws.domains]
        domains = sorted(list(set(domains)))

        if self.find('new-mailbox-domain'):
            self.find('new-mailbox-domain').labels = \
                domains + [_('Custom domain')]
            self.find('new-mailbox-domain').values = domains + [None]

            if self.manager.is_configured:
                self.binder.unpopulate().populate()

        if os.path.exists(self.manager.config.dkim_private_key):
            pubkey = subprocess.check_output([
                'openssl', 'rsa', '-in', self.manager.config.dkim_private_key,
                '-pubout'
            ])
            pubkey = filter(None, pubkey.split('-'))[1].replace('\n', '')
            dns = '@\t\t\t\t10800 IN TXT "v=spf1 a -all"\n'
            dns += '_domainkey\t\t10800 IN TXT "o=~; r=postmaster@<domain>"\n'
            dns += '%s._domainkey\t10800 IN TXT "v=DKIM1; k=rsa; p=%s"\n' % (
                self.manager.config.dkim_selector,
                pubkey
            )
            dns += '_dmarc\t\t\t10800 IN TXT "v=DMARC1; p=quarantine"\n'

            self.find('dkim-domain-entry').value = dns
        else:
            self.find('dkim-domain-entry').value = _('No valid key exists')

    @on('generate-dkim-key', 'click')
    def on_generate_dkim_key(self):
        self.binder.update()
        self.manager.generate_dkim_key()
        self.binder.populate()
        self.save()

    @on('generate-tls-cert', 'click')
    def on_generate_tls_cert(self):
        self.binder.update()
        self.manager.generate_tls_cert()
        self.binder.populate()
        self.save()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.manager.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #38
0
class Configurator(SectionPlugin):
    def init(self):
        self.title = _('Configure')
        self.icon = 'wrench'
        self.category = ''
        self.order = 50

        self.append(self.ui.inflate('configurator:main'))

        self.binder = Binder(ajenti.config.tree, self.find('ajenti-config'))

        self.ccmgr = ClassConfigManager.get()
        self.classconfig_binding = Binder(self.ccmgr,
                                          self.find('classconfigs'))

        def post_classconfig_bind(object, collection, item, ui):
            def configure():
                self.configure_plugin(item, notify=False)

            ui.find('configure').on('click', configure)

        self.find('classconfigs').find(
            'classes').post_item_bind = post_classconfig_bind

        self.find('users').new_item = lambda c: UserData()

        def post_user_bind(object, collection, item, ui):
            provider = UserManager.get(manager.context).get_sync_provider()
            editable = item.name != 'root'
            renameable = editable and provider.allows_renaming
            deletable = renameable

            ui.find('name-edit').visible = renameable
            ui.find('name-label').visible = not renameable
            ui.find('delete').visible = deletable

            box = ui.find('permissions')
            box.empty()

            p = PermissionProvider.get_all()
            for prov in p:
                line = self.ui.create('tab', title=prov.get_name())
                box.append(line)
                for perm in prov.get_permissions():
                    line.append(
                        self.ui.create('checkbox',
                                       id=perm[0],
                                       text=perm[1],
                                       value=(perm[0] in item.permissions)))

            def copy():
                self.save()
                newuser = deepcopy(item)
                newuser.name += '_'
                collection[newuser.name] = newuser
                self.refresh()

            ui.find('copy').on('click', copy)

        self.find('users').post_item_bind = post_user_bind

        def post_user_update(object, collection, item, ui):
            box = ui.find('permissions')
            for prov in PermissionProvider.get_all():
                for perm in prov.get_permissions():
                    has = box.find(perm[0]).value
                    if has and not perm[0] in item.permissions:
                        item.permissions.append(perm[0])
                    if not has and perm[0] in item.permissions:
                        item.permissions.remove(perm[0])
            if ui.find('password').value:
                item.password = ui.find('password').value

        self.find('users').post_item_update = post_user_update

    def on_page_load(self):
        self.refresh()

    @on('sync-users-button', 'click')
    def on_sync_users(self):
        self.save()
        prov = UserManager.get(manager.context).get_sync_provider()
        try:
            prov.test()
            prov.sync()
        except Exception as e:
            self.context.notify('error', str(e))

        self.refresh()

    @on('configure-sync-button', 'click')
    def on_configure_sync(self):
        self.save()
        self.configure_plugin(UserManager.get(
            manager.context).get_sync_provider(),
                              notify=False)
        self.refresh()

    def refresh(self):
        self.binder.unpopulate()

        self.find('sync-providers').labels = [
            x.title for x in UserSyncProvider.get_classes()
        ]
        self.find('sync-providers').values = [
            x.id for x in UserSyncProvider.get_classes()
        ]

        provider = UserManager.get(manager.context).get_sync_provider()
        self.find('sync-providers').value = provider.id
        self.find('add-user-button').visible = provider.id == ''
        self.find('sync-users-button').visible = provider.id != ''
        self.find('password').visible = provider.id == ''
        self.find('configure-sync-button'
                  ).visible = provider.classconfig_editor is not None

        try:
            provider.test()
            sync_ok = True
        except Exception as e:
            self.context.notify('error', str(e))
            sync_ok = False

        self.find('sync-status-ok').visible = sync_ok
        self.find('sync-status-fail').visible = not sync_ok

        languages = sorted(ajenti.locales.list_locales())
        self.find('language').labels = [_('Auto'), 'en_US'] + languages
        self.find('language').values = ['', 'en_US'] + languages

        self.binder.setup().populate()
        self.ccmgr.reload()
        self.classconfig_binding.setup().populate()

    @on('save-button', 'click')
    @restrict('configurator:configure')
    def save(self):
        self.binder.update()

        UserManager.get(manager.context).set_sync_provider(
            self.find('sync-providers').value)

        for user in ajenti.config.tree.users.values():
            if not '|' in user.password:
                user.password = UserManager.get(manager.context).hash_password(
                    user.password)

        self.refresh()
        ajenti.config.save()
        self.context.notify(
            'info',
            _('Saved. Please restart Ajenti for changes to take effect.'))

    @on('fake-ssl', 'click')
    def on_gen_ssl(self):
        host = self.find('fake-ssl-host').value
        if host == '':
            self.context.notify('error', _('Please supply hostname'))
        else:
            self.gen_ssl(host)

    @on('restart-button', 'click')
    def on_restart(self):
        ajenti.restart()

    @intent('configure-plugin')
    def configure_plugin(self, plugin=None, notify=True):
        self.find('tabs').active = 1
        self.refresh()

        if plugin and notify:
            self.context.notify(
                'info',
                _('Please configure %s plugin!') %
                plugin.classconfig_editor.title)

        self.activate()

        dialog = self.find('classconfigs').find('dialog')
        dialog.find('container').empty()
        dialog.visible = True

        editor = plugin.classconfig_editor.new(self.ui)
        dialog.find('container').append(editor)
        binder = DictAutoBinding(plugin, 'classconfig', editor.find('bind'))
        binder.populate()

        def save(button=None):
            dialog.visible = False
            binder.update()
            plugin.save_classconfig()
            self.save()

        dialog.on('button', save)

    @intent('setup-fake-ssl')
    def gen_ssl(self, host):
        self.save()
        subprocess.call(['ajenti-ssl-gen', host, '-f'])
        ajenti.config.load()
        self.refresh()
Пример #39
0
class BIND9Plugin(SectionPlugin):
    config_root = platform_select(
        debian='/etc/bind/',
        centos='/etc/named/',
    )

    config_path = platform_select(
        debian='/etc/bind/named.conf',
        centos='/etc/named.conf',
    )

    def init(self):
        self.title = 'BIND9'
        self.icon = 'globe'
        self.category = _('Software')

        self.append(self.ui.inflate('bind9:main'))

        self.config = BIND9Config(path=self.config_path)
        self.binder = Binder(None, self)
        self.find('zones').new_item = lambda c: ZoneData()

        def post_zone_bind(o, c, i, u):
            path = i.file
            if path is not None:
                if not path.startswith('/'):
                    path = self.config_root + path
                exists = os.path.exists(path)
            else:
                exists = False
            u.find('no-file').visible = not exists
            u.find('file').visible = exists
            if exists:
                u.find('editor').value = open(path).read()

            def on_save_zone():
                open(path, 'w').write(u.find('editor').value)
                self.context.notify('info', _('Zone saved'))

            def on_create_zone():
                open(path, 'w').write("""$TTL    604800
@       IN      SOA     ns. root.ns. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@                   IN      NS      ns.
example.com.        IN      A       127.0.0.1
example.com.        IN      AAAA    ::1
""")
                post_zone_bind(o, c, i, u)

            u.find('save-zone').on('click', on_save_zone)
            u.find('create-zone').on('click', on_create_zone)

        self.find('zones').post_item_bind = post_zone_bind

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.binder.setup(self.config.tree).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #40
0
class Users(SectionPlugin):
    def init(self):
        self.title = _('Users')
        self.icon = 'group'
        self.category = _('System')
        self.append(self.ui.inflate('users:main'))

        def _filterOnlyUsers(x):
            u = int(x.uid)
            return u >= 500

        def _filterOnlySystemUsers(x):
            u = int(x.uid)
            return u < 500

        def _sorter(x):
            g = int(x.gid)
            if g >= 500:
                return g - 10000
            return g

        self.find('users').filter = _filterOnlyUsers
        self.find('system-users').filter = _filterOnlySystemUsers
        self.find('groups').sorting = _sorter

        self.config = PasswdConfig(path='/etc/passwd')
        self.config_g = GroupConfig(path='/etc/group')
        self.binder = Binder(None, self.find('passwd-config'))
        self.binder_system = Binder(None, self.find('passwd-config-system'))
        self.binder_g = Binder(None, self.find('group-config'))

        self.mgr = UsersBackend.get()

        def post_item_bind(object, collection, item, ui):
            ui.find('change-password').on('click', self.change_password, item,
                                          ui)
            ui.find('remove-password').on('click', self.remove_password, item)
            if not os.path.exists(item.home):
                ui.find('create-home-dir').on('click', self.create_home_dir,
                                              item, ui)
                ui.find('create-home-dir').visible = True

        self.find('users').post_item_bind = post_item_bind
        self.find('system-users').post_item_bind = post_item_bind

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.config_g.load()

        self.binder.setup(self.config.tree).populate()
        self.binder_system.setup(self.config.tree).populate()

        self.binder_g.setup(self.config_g.tree)
        self.find('group-members').labels = self.find(
            'group-members').values = [x.name for x in self.config.tree.users]
        self.binder_g.populate()

    @on('add-user', 'click')
    def on_add_user(self):
        self.find('input-username').visible = True

    @on('input-username', 'submit')
    def on_add_user_done(self, value):
        self.mgr.add_user(value)
        self.refresh()

    @on('add-group', 'click')
    def on_add_group(self):
        self.find('input-groupname').visible = True

    @on('input-groupname', 'submit')
    def on_add_group_done(self, value):
        self.mgr.add_group(value)
        self.refresh()

    @on('save-users', 'click')
    def save_users(self):
        self.binder.update()
        self.config.save()

    @on('save-system-users', 'click')
    def save_system_users(self):
        self.binder_system.update()
        self.config.save()

    @on('save-groups', 'click')
    def save_groups(self):
        self.binder_g.update()
        self.config_g.save()

    def create_home_dir(self, user, ui):
        self.mgr.make_home_dir(user)
        self.context.notify('info',
                            _('Home dir for %s was created') % user.name)
        ui.find('create-home-dir').visible = False

    def change_password(self, user, ui):
        new_password = ui.find('new-password').value

        if new_password:
            try:
                self.mgr.change_password(user, new_password)
                self.context.notify(
                    'info',
                    _('Password for %s was changed') % user.name)
                ui.find('new-password').value = ''
            except Exception, e:
                self.context.notify('error', _('Error: "%s"') % e.message)
        else:
Пример #41
0
class FileManager (SectionPlugin):
    default_classconfig = {'root': '/', 'start': '/'}
    classconfig_editor = FileManagerConfigEditor
    classconfig_root = True

    def init(self):
        self.title = _('Файлов мениджър')
        self.category = _('Tools')
        self.icon = 'folder-open'

        self.backend = FMBackend().get()

        self.append(self.ui.inflate('fm:main'))
        self.controller = Controller()

        def post_item_bind(object, collection, item, ui):
            ui.find('name').on('click', self.on_item_click, object, item)
            ui.find('edit').on('click', self.edit, item.fullpath)
        self.find('items').post_item_bind = post_item_bind

        def post_bc_bind(object, collection, item, ui):
            ui.find('name').on('click', self.on_bc_click, object, item)
        self.find('breadcrumbs').post_item_bind = post_bc_bind

        self.clipboard = []
        self.tabs = self.find('tabs')

    def on_first_page_load(self):
        self.new_tab()
        self.binder = Binder(self.controller, self.find('filemanager')).populate()
        self.binder_c = Binder(self, self.find('bind-clipboard')).populate()

    def on_page_load(self):
        self.refresh()

    def refresh_clipboard(self):
        self.binder_c.setup().populate()

    @on('tabs', 'switch')
    def on_tab_switch(self):
        if self.tabs.active == (len(self.controller.tabs) - 1):
            self.new_tab()
        self.refresh()

    @intent('fm:browse')
    def new_tab(self, path=None):
        dir = path or self.classconfig.get('start', None) or '/'
        if not dir.startswith(self.classconfig['root']):
            dir = self.classconfig['root']
        self.controller.new_tab(dir)
        if not self.active:
            self.activate()

    @on('close', 'click')
    def on_tab_close(self):
        if len(self.controller.tabs) > 2:
            self.controller.tabs.pop(self.tabs.active)
        self.tabs.active = 0
        self.refresh()

    @on('new-file', 'click')
    def on_new_file(self):
        destination = self.controller.tabs[self.tabs.active].path
        logging.info('[fm] new file in %s' % destination)
        path = os.path.join(destination, 'new file')
        try:
            open(path, 'w').close()
            self._chown_new(path)
        except OSError as e:
            self.context.notify('error', str(e))
        self.refresh()

    @on('new-dir', 'click')
    def on_new_directory(self):
        destination = self.controller.tabs[self.tabs.active].path
        logging.info('[fm] new directory in %s' % destination)
        path = os.path.join(destination, 'new directory')
        if not os.path.exists(path):
            try:
                os.mkdir(path)
                os.chmod(path, 0755)
                self._chown_new(path)
            except OSError as e:
                self.context.notify('error', str(e))
        self.refresh()

    def _chown_new(self, path):
        uid = self.classconfig.get('new_owner', 'root') or 'root'
        gid = self.classconfig.get('new_group', 'root') or 'root'
        try:
            uid = int(uid)
        except:
            uid = pwd.getpwnam(uid)[2]
        try:
            gid = int(gid)
        except:
            gid = grp.getgrnam(gid)[2]
        os.chown(path, uid, gid)

    def upload(self, name, file):
        destination = self.controller.tabs[self.tabs.active].path
        logging.info('[fm] uploading %s to %s' % (name, destination))
        try:
            output = open(os.path.join(destination, name), 'w')
            while True:
                data = file.read(1024 * 1024)
                if not data:
                    break
                gevent.sleep(0)
                output.write(data)
            output.close()
        except OSError as e:
            self.context.notify('error', str(e))
        self.refresh()

    @on('mass-cut', 'click')
    def on_cut(self):
        l = self._get_checked()
        for i in l:
            i.action = 'cut'
        self.clipboard += l
        self.refresh_clipboard()

    @on('mass-copy', 'click')
    def on_copy(self):
        l = self._get_checked()
        for i in l:
            i.action = 'copy'
        self.clipboard += l
        self.refresh_clipboard()

    @on('mass-delete', 'click')
    def on_delete(self):
        def callback(task):
            self.context.notify('info', _('Files deleted'))
            self.refresh()
        self.backend.remove(self._get_checked(), cb=callback)

    @on('paste', 'click')
    def on_paste(self):
        tab = self.controller.tabs[self.tabs.active]
        for_move = []
        for_copy = []
        for i in self.clipboard:
            if i.action == 'cut':
                for_move.append(i)
            else:
                for_copy.append(i)

        try:
            if for_move:
                def callback(task):
                    self.context.notify('info', _('Files moved'))
                    self.refresh()
                self.backend.move(for_move, tab.path, callback)
            if for_copy:
                def callback(task):
                    self.context.notify('info', _('Files copied'))
                    self.refresh()
                self.backend.copy(for_copy, tab.path, callback)
            self.clipboard = []
        except Exception as e:
            self.context.notify('error', str(e))
        self.refresh_clipboard()

    @on('select-all', 'click')
    def on_select_all(self):
        self.binder.update()
        tab = self.controller.tabs[self.tabs.active]
        for item in tab.items:
            item.checked = not item.checked
        self.binder.populate()
        self.context.notify('info', _('Selected %i items') % len(tab.items)) 

    def _get_checked(self):
        self.binder.update()
        tab = self.controller.tabs[self.tabs.active]
        r = []
        for item in tab.items:
            if item.checked:
                r.append(item)
                item.checked = False
        self.refresh()
        return r

    @on('clear-clipboard', 'click')
    def on_clear_clipboard(self):
        self.clipboard = []
        self.refresh_clipboard()

    def on_item_click(self, tab, item):
        path = os.path.join(tab.path, item.name)
        if not os.path.isdir(path):
            self.edit(path)
        if not path.startswith(self.classconfig['root']):
            return
        tab.navigate(path)
        self.refresh()

    def edit(self, path):
        self.find('dialog').visible = True
        self.item = Item(path)
        self.item.read()
        self.binder_d = Binder(self.item, self.find('dialog')).populate()

        # Unpack
        u = Unpacker.find(self.item.fullpath.lower())
        unpack_btn = self.find('dialog').find('unpack')
        unpack_btn.visible = u is not None

        def cb():
            self.context.notify('info', _('Unpacked'))
            self.refresh()

        def unpack():
            u.unpack(self.item.fullpath, cb=cb)
            logging.info('[fm] unpacking %s' % self.item.fullpath)

        unpack_btn.on('click', lambda: unpack())

        # Edit
        edit_btn = self.find('dialog').find('edit')
        if self.item.size > 1024 * 1024 * 5:
            edit_btn.visible = False

        def edit():
            self.context.launch('notepad', path=self.item.fullpath)

        edit_btn.on('click', lambda: edit())

    @on('dialog', 'button')
    def on_close_dialog(self, button):
        self.find('dialog').visible = False
        if button == 'save':
            self.binder_d.update()
            try:
                self.item.write()
            except Exception as e:
                self.context.notify('error', str(e))
            self.refresh()

            if self.find('chmod-recursive').value:
                cmd = 'chown -Rv "%s:%s" "%s"; chmod -Rv %o "%s"' % (
                    self.item.owner, self.item.group,
                    self.item.fullpath,
                    self.item.mode,
                    self.item.fullpath,
                )
                self.context.launch('terminal', command=cmd)

            logging.info('[fm] modifying %s: %o %s:%s' % (self.item.fullpath, self.item.mode, self.item.owner, self.item.group))

    def on_bc_click(self, tab, item):
        if not item.path.startswith(self.classconfig['root']):
            return
        tab.navigate(item.path)
        self.refresh()

    def refresh(self, _=None):
        for tab in self.controller.tabs:
            tab.refresh()
        self.binder.populate()
Пример #42
0
class NTPDPlugin(SectionPlugin):
    def get_tz_debian(self):
        return open('/etc/timezone').read().strip()

    def get_tz_nondebian(self):
        return os.path.realpath(
            '/etc/localtime')[len('/usr/share/zoneinfo/'):] if os.path.islink(
                '/etc/localtime') else ''

    def set_tz_debian(self, timezone):
        open('/etc/timezone', 'w').write(timezone + '\n')

    def set_tz_nondebian(self, timezone):
        tz = os.path.join('/usr/share/zoneinfo/', timezone)
        if os.path.exists('/etc/localtime'):
            os.unlink('/etc/localtime')
        os.symlink(tz, '/etc/localtime')

    openntpd = isopenntpd()

    service_name = platform_select(
        default='openntpd' if openntpd else 'ntp',
        centos='openntpd' if openntpd else 'ntpd',
        mageia='ntpd',
    )

    get_tz = platform_select(
        debian=get_tz_debian,
        default=get_tz_nondebian,
    )

    set_tz = platform_select(
        debian=set_tz_debian,
        default=set_tz_nondebian,
    )

    def init(self):
        self.title = _('Date & Time')
        self.icon = 'time'
        self.category = _('System')

        self.append(self.ui.inflate('ntpd:main'))

        self.find('servicebar').name = self.service_name
        self.find('servicebar').reload()

        self.config = NTPDConfig(path=platform_select(
            default=open_ntpd_conf if self.openntpd else ntpd_conf,
            centos='/usr/local/etc/ntpd.conf' if self.openntpd else ntpd_conf,
            freebsd='/usr/local/etc/ntpd.conf' if self.openntpd else ntpd_conf,
        ))

        self.binder = Binder(None, self)

        self.available_zones = []
        for d, dirs, files in os.walk('/usr/share/zoneinfo',
                                      followlinks=False):
            for f in files:
                if f != 'zone.tab':
                    self.available_zones.append(os.path.join(d, f))
        self.available_zones = [
            x[len('/usr/share/zoneinfo/'):] for x in self.available_zones
        ]
        self.available_zones.sort()

        self.find('servers').new_item = lambda c: ServerData()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.now = int(time.time())
        self.timezone = self.get_tz()
        self.binder.setup(self).populate()

    @on('set', 'click')
    def on_set(self):
        self.binder.update()
        d = datetime.fromtimestamp(self.now)
        s = d.strftime('%m%d%H%M%Y')
        self.set_tz(self.timezone)
        subprocess.call(['date', s])
        self.refresh()

    @on('sync', 'click')
    def on_sync(self):
        self.binder.update()
        if len(self.config.tree.servers) == 0:
            self.context.notify('error', _('No servers defined'))
            return
        server = self.config.tree.servers[0].address
        output = subprocess.check_output(['ntpdate', '-u', server])
        self.context.notify('info', _('Done'))
        self.context.notify('info', output)
        self.refresh()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
        ServiceMultiplexor.get().get_one(self.service_name).restart()
Пример #43
0
class MailPlugin(SectionPlugin):
    def init(self):
        self.title = _('Mail')
        self.icon = 'envelope'
        self.category = 'Web'

        self.manager = MailManager.get()

        if not self.manager.is_configured:
            self.append(self.ui.inflate('vh-mail:not-configured'))
        else:
            self.post_init()

    @on('initial-enable', 'click')
    def on_initial_enable(self):
        self.post_init()
        self.manager.save()
        self.refresh()

    def post_init(self):
        self.empty()
        self.append(self.ui.inflate('vh-mail:main'))

        self.binder = Binder(None, self)

        def post_mb_bind(object, collection, item, ui):
            ui.find('size').text = str_fsize(self.manager.get_usage(item))

        def post_mb_update(object, collection, item, ui):
            if ui.find('password').value:
                item.password = ui.find('password').value

        self.find('mailboxes').post_item_bind = post_mb_bind
        self.find('mailboxes').post_item_update = post_mb_update

        self.binder.setup(self.manager.config)

    @on('new-mailbox', 'click')
    def on_new_mailbox(self):
        self.binder.update()
        mb = Mailbox.create()
        mb.local = self.find('new-mailbox-local').value
        mb.domain = self.find('new-mailbox-domain').value or self.find(
            'new-mailbox-domain-custom').value
        mb.password = ''
        self.find('new-mailbox-local').value = ''
        if not mb.local:
            self.context.error(_('Invalid mailbox name'))
        if not mb.domain:
            self.context.error(_('Invalid mailbox domain'))

        self.manager.config.mailboxes.append(mb)
        self.manager.save()
        self.binder.populate()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        domains = []
        for ws in VHManager.get().config.websites:
            domains += [d.domain for d in ws.domains]
        domains = sorted(list(set(domains)))

        self.find('new-mailbox-domain').labels = domains + [_('Custom domain')]
        self.find('new-mailbox-domain').values = domains + [None]

        if self.manager.is_configured:
            self.binder.unpopulate().populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.manager.save()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #44
0
class GitPlugin (SectionPlugin):
    def init(self):
        self.title = _('Git')
        self.icon = 'folder-close'
        self.category = 'Software'
        self.append(self.ui.inflate('git:main'))
        self.binder = Binder(None, self)
        self.user = None

        # Find User
        for user in ['gitolite3', 'gitolite2', 'gitolite']:
             try:
                 pwn = getpwnam(user)
                 self.user = pwn
                 break
             except:
                 pass

        if self.user == None:
            exit(-1)

        # Set Conf & Key Directory
        self.confdir = os.path.join(self.user.pw_dir, '.gitolite/conf/')
        self.keydir = os.path.join(self.user.pw_dir, '.gitolite/keydir/')

        # Event for new items
        self.find('usercollection').new_item = lambda c: User('newuser', '')
        self.find('repocollection').new_item = lambda c: Repository('newrepo')

        # Remove old Gitolite.conf and replace our version
        os.remove(self.confdir + 'gitolite.conf')
        with open((self.confdir + 'gitolite.conf'), 'w+') as file:
            file.write('include "*.conf"')

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.refresh_users()
        self.refresh_repositories()
        self.binder.setup(self).populate()

    def reload(self):
        subprocess.call('su -c "gitolite compile" ' + self.user.pw_name, shell=True)
        self.context.notify('info', _('Saved'))

    #
    # Repositories
    #
    def refresh_repositories(self):
        self.repositories = []

        userlist = []
        for user in self.users:
            userlist.append(user.name)
        self.find('rwusers').values = userlist
        self.find('rwusers').labels = userlist
        self.find('rousers').values = userlist
        self.find('rousers').labels = userlist

        for file in os.listdir(self.confdir):
            if os.path.isfile(self.confdir + file) and not 'gitolite.conf' in file:
                with open(self.confdir + file) as content:
                    content = content.read()
                    repo = Repository('')

                    # Repository Name
                    match = re.search(r'repo[\s]+([A-z0-9]+)', content, re.I)
                    repo.name = match.group(1)

                    #Repository Users(read-write)
                    match = re.search(r'RW\+[\s]+\=[\s]+([^\n]+)', content, re.I)
                    repo.rwusers = match is not None and match.group(1) or ''

                    #Repository Users(read-only)
                    match = re.search(r'R[\s]+\=[\s]+([^\n]+)', content, re.I)
                    repo.rousers = match is not None and match.group(1) or ''

                    self.repositories.append(repo)

    @on('save-repositories', 'click')
    def save_repositories(self):
        self.binder.update()

        files = []
        for file in os.listdir(self.confdir):
            if os.path.isfile(self.confdir + file) and not "gitolite.conf" in file:
                files.append(file)

        for repo in self.repositories:
            with open((self.confdir + repo.name + '.conf'), 'w+') as file:
                content = 'repo '
                content += repo.name
                content += '\n'

                rwusers = repo.rwusers.strip()
                if len(rwusers) > 0:
                    content += '\tRW+\t=\t' + rwusers + '\n'

                rousers = repo.rousers.strip()
                if len(rousers) > 0:
                    content += '\tR\t=\t' + rousers + '\n'

                file.write(content)

            if (repo.name + '.conf') in files:
                files.remove(repo.name + '.conf')

        for file in files:
            os.remove(self.confdir + file)

        self.reload()

    #
    # Users
    #
    def refresh_users(self):
        self.users = []

        for file in os.listdir(self.keydir):
            with open(self.keydir + file) as content:
                username = file.replace('.pub', '')
                pubkey = content.read()

                self.users.append(User(username, pubkey))

    @on('save-users', 'click')
    def save_users(self):
        self.binder.update()

        files = []
        for file in os.listdir(self.keydir):
            files.append(file)

        for user in self.users:
            with open((self.keydir + user.name + '.pub'), 'w+') as file:
                file.write(user.pubkey)
            if (user.name + '.pub') in files:
                files.remove(user.name + '.pub')

        for file in files:
            os.remove(self.keydir + file)

        self.reload();
Пример #45
0
class WebserverPlugin(SectionPlugin):
    service_name = ''
    service_buttons = []
    hosts_available_dir = ''
    hosts_enabled_dir = ''
    hosts_dir = None
    template = ''
    supports_host_activation = True
    configurable = False
    main_conf_files = []
    configurations = []

    def log(self, msg):
        logging.info('[%s] %s' % (self.service_name, msg))

    def init(self):
        self.append(self.ui.inflate('webserver_common:main'))
        self.binder = Binder(None, self)
        self.find_type('servicebar').buttons = self.service_buttons

        # Hosts preperation

        self.hosts_dir = AvailabilitySymlinks(self.hosts_available_dir,
                                              self.hosts_enabled_dir,
                                              self.supports_host_activation)

        def delete_host(host, c):
            self.log('removed host %s' % host.name)
            c.remove(host)
            self.hosts_dir.delete(host.name)

        def on_host_bind(o, c, host, u):
            host.__old_name = host.name

        def on_host_update(o, c, host, u):
            if host.__old_name != host.name:
                self.log('renamed host %s to %s' %
                         (host.__old_name, host.name))
                self.hosts_dir.rename(host.__old_name, host.name)
            host.save()

        def new_host(c):
            name = 'untitled'
            while os.path.exists(self.hosts_dir.get_path(name)):
                name += '_'
            self.log('created host %s' % name)
            self.hosts_dir.open(name, 'w').write(self.template)
            return WebserverConf(self, self.hosts_dir, name)

        self.find('hosts').delete_item = delete_host
        self.find('hosts').new_item = new_host
        self.find('hosts').post_item_bind = on_host_bind
        self.find('hosts').post_item_update = on_host_update
        self.find('header-active-checkbox').visible = \
            self.find('body-active-line').visible = \
            self.supports_host_activation

        def on_conf_bind(o, c, conf, u):
            conf.__old_name = conf.name

        def on_conf_update(o, c, conf, u):
            conf.save()

        self.find('configurations').post_item_bind = on_conf_bind
        self.find('configurations').post_item_update = on_conf_update

        self.tabs = self.find('tabs')

    def on_page_load(self):
        self.refresh()

    @on('save-button', 'click')
    def save(self):
        self.log('saving hosts')
        self.binder.update()
        self.refresh()
        self.context.notify('info', 'Saved')

    def refresh(self):
        self.hosts = [
            WebserverConf(self, self.hosts_dir, x)
            for x in self.hosts_dir.list_available()
        ]
        self.configurations = [
            WebserverMainConf(y).update() for y in self.main_conf_files
        ] if self.configurable else []

        self.binder.setup(self).populate()
        self.find_type('servicebar').reload()
Пример #46
0
class CSFSection(SectionPlugin):
    def init(self):
        self.title = _('CSF Firewall')
        self.icon = 'fire'
        self.category = _('System')
        self.backend = CSFBackend.get()

        self.append(self.ui.inflate('csf:main'))

        self.config = CSFConfig(path='/etc/csf/csf.conf')
        self.list_allow = []
        self.list_deny = []
        self.list_tempallow = []
        self.list_tempban = []

        def delete_rule(csf_option, i):
            self.save()
            subprocess.call(['csf', csf_option, i.value.split('#')[0]])
            self.refresh()

        self.find('list_allow').on_delete = lambda i, c: delete('-ar', i)
        self.find('list_deny').on_delete = lambda i, c: delete('-dr', i)
        self.find('list_tempallow').on_delete = lambda i, c: delete('-tr', i)
        self.find('list_tempban').on_delete = lambda i, c: delete('-tr', i)

        def add_rule(csf_option, address):
            self.save()
            p = subprocess.Popen(['csf', csf_option, address],
                                 stdout=subprocess.PIPE)
            o, e = p.communicate()
            self.context.notify('info', o)
            self.refresh()

        self.find('list_allow-add').on(
            'click',
            lambda: add_rule('-a',
                             self.find('permanent-lists-add-address').value))
        self.find('list_deny-add').on(
            'click',
            lambda: add_rule('-d',
                             self.find('permanent-lists-add-address').value))
        self.find('list_tempallow-add').on(
            'click',
            lambda: add_rule('-ta',
                             self.find('temporary-lists-add-address').value))
        self.find('list_tempban-add').on(
            'click',
            lambda: add_rule('-td',
                             self.find('temporary-lists-add-address').value))

        self.binder = Binder(None, self)
        self.binder_lists = Binder(self, self.find('lists'))

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.list_allow = self.backend.read_list('allow')
        self.list_deny = self.backend.read_list('deny')
        self.list_tempallow = self.backend.read_list('tempallow')
        self.list_tempban = self.backend.read_list('tempban')
        self.binder.setup(self.config.tree).populate()
        self.binder_lists.populate()

    @on('apply', 'click')
    def on_apply(self):
        self.backend.apply()
        self.context.notify('info', _('Applied'))

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.backend.write_list('allow', self.list_allow)
        self.backend.write_list('deny', self.list_deny)
        self.backend.write_list('tempallow', self.list_tempallow)
        self.backend.write_list('tempban', self.list_tempban)

        self.binder.setup(self.config.tree).populate()
        self.context.notify('info', _('Saved'))
        try:
            self.backend.test_config()
            self.context.notify('info', _('Self-test OK'))
        except Exception as e:
            self.context.notify('error', str(e))
Пример #47
0
class WebsitesPlugin(SectionPlugin):
    def init(self):
        self.title = _('Websites')
        self.icon = 'globe'
        self.category = 'Web'
        self.order = 1

        self.manager = VHManager.get()

        if not self.manager.is_configured:
            from ajenti.plugins.vh import destroyed_configs
            self.append(self.ui.inflate('vh:not-configured'))
            self.find('destroyed-configs').text = ', '.join(destroyed_configs)
        else:
            self.post_init()

    @on('initial-enable', 'click')
    def on_initial_enable(self):
        self.post_init()
        self.manager.save()
        self.refresh()

    def post_init(self):
        self.empty()
        self.append(self.ui.inflate('vh:main'))

        self.binder = Binder(None, self)

        def post_ws_bind(object, collection, item, ui):
            def manage():
                self.context.launch('v:manage-website', website=item)

            ui.find('manage').on('click', manage)

        self.find('websites').post_item_bind = post_ws_bind
        self.find(
            'websites').filter = lambda ws: self.context.session.identity in [
                'root', ws.owner
            ]

        self.binder.setup(self.manager)

    @on('new-website', 'click')
    def on_new_website(self):
        self.binder.update()
        name = self.find('new-website-name').value
        self.find('new-website-name').value = ''
        if not name:
            name = '_'

        slug = slugify(name)
        slugs = [x.slug for x in self.manager.config.websites]
        while slug in slugs:
            slug += '_'

        w = Website.create(name)
        w.slug = slug
        w.owner = self.context.session.identity
        self.manager.config.websites.append(w)
        self.manager.save()
        self.binder.populate()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        if self.manager.is_configured:
            self.manager.config.websites = sorted(self.manager.config.websites,
                                                  key=lambda x: x.name)
            self.binder.setup().populate()

    @on('recheck', 'click')
    def on_recheck(self):
        self.binder.update()
        self.context.endpoint.send_progress(_('Testing configuration'))
        try:
            self.manager.run_checks()
        finally:
            self.context.endpoint.send_progress(None)
            self.refresh()

    @on('save', 'click')
    def save(self):
        self.context.endpoint.send_progress(_('Saving changes'))
        self.binder.update()
        self.manager.save()
        self.context.endpoint.send_progress(_('Applying changes'))
        self.manager.update_configuration()
        self.context.endpoint.send_progress(_('Restarting web services'))
        self.manager.restart_services()
        gevent.spawn(self.on_recheck)
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #48
0
class SNMPDPlugin(SectionPlugin):
    service_name = platform_select(default='snmpd', )

    def init(self):
        self.title = 'SNMP'
        self.icon = 'exchange'
        self.category = _('Software')

        self.append(self.ui.inflate('snmpd:main'))

        self.find('servicebar').name = self.service_name
        self.find('servicebar').reload()

        self.snmp_config = SNMPConfig(
            path=platform_select(default='/etc/snmp/snmp.conf', ))
        self.snmpd_config = SNMPDConfig(
            path=platform_select(default='/etc/snmp/snmpd.conf', ))

        self.find('rocommunities').new_item = lambda c: ROCommunityData()
        self.find('rwcommunities').new_item = lambda c: RWCommunityData()
        self.find('sinks1').new_item = lambda c: Sink1Data()
        self.find('sinks2').new_item = lambda c: Sink2Data()
        self.find('sinks2c').new_item = lambda c: Sink2cData()

        self.binder = Binder(None, self)

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.snmp_config.load()
        self.snmpd_config.load()

        self.rocommunities = self.snmpd_config.tree.rocommunities
        self.rwcommunities = self.snmpd_config.tree.rwcommunities
        self.sinks1 = self.snmpd_config.tree.sinks1
        self.sinks2 = self.snmpd_config.tree.sinks2
        self.sinks2c = self.snmpd_config.tree.sinks2c

        enabled_mibs = []
        for mib in self.snmp_config.tree.mibs:
            for x in mib.name.strip('-+:').split(':'):
                enabled_mibs.append(x)
        self.mibs = []
        for dirpath, dirname, filenames in os.walk('/usr/share/mibs',
                                                   followlinks=True):
            for x in filenames:
                if not x.startswith('.'):
                    mib = MIBData()
                    mib.name = x
                    mib.selected = x in enabled_mibs
                    self.mibs.append(mib)
        self.mibs = sorted(self.mibs, key=lambda x: x.name)
        self.binder.setup(self).populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()

        mib = MIBData()
        mib.name = ':'.join([x.name for x in self.mibs if x.selected])
        for x in list(self.snmp_config.tree.mibs):
            self.snmp_config.tree.mibs.remove(x)
        self.snmp_config.tree.mibs.append(mib)

        self.snmp_config.save()
        self.snmpd_config.save()

        self.refresh()
        self.context.notify('info', _('Saved'))
        ServiceMultiplexor.get().get_one(self.service_name).restart()
Пример #49
0
class WebsitesPlugin(SectionPlugin):
    def init(self):
        self.title = _('Websites')
        self.icon = 'globe'
        self.category = 'Web'

        self.manager = VHManager.get()

        if not self.manager.is_configured:
            from ajenti.plugins.vh import destroyed_configs
            self.append(self.ui.inflate('vh:not-configured'))
            self.find('destroyed-configs').text = ', '.join(destroyed_configs)
        else:
            self.post_init()

    @on('initial-enable', 'click')
    def on_initial_enable(self):
        self.post_init()
        self.manager.save()
        self.refresh()

    def post_init(self):
        self.empty()
        self.append(self.ui.inflate('vh:main'))

        self.binder = Binder(None, self)

        def post_ws_bind(object, collection, item, ui):
            def manage():
                self.context.launch('v:manage-website', website=item)

            ui.find('manage').on('click', manage)

        self.find('websites').post_item_bind = post_ws_bind

        self.binder.setup(self.manager.config)

    @on('new-website', 'click')
    def on_new_website(self):
        self.binder.update()
        name = self.find('new-website-name').value
        self.find('new-website-name').value = ''
        if not name:
            name = '_'

        slug = slugify(name)
        slugs = [x.slug for x in self.manager.config.websites]
        while slug in slugs:
            slug += '_'

        w = Website.create(name)
        w.slug = slug
        self.manager.config.websites.append(w)
        self.manager.save()
        self.binder.populate()

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        if self.manager.is_configured:
            self.binder.unpopulate().populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.context.endpoint.send_progress(_('Saving changes'))
        self.manager.save()
        self.context.endpoint.send_progress(_('Applying changes'))
        self.manager.update_configuration()
        self.refresh()
        self.context.notify('info', _('Saved'))
Пример #50
0
class Test(SectionPlugin):
    def init(self):
        self.title = 'Instance Manager'
        self.icon = 'th-list'
        self.category = 'System'
        self.mgr = lxc.lxcman()

        #change 'dialogging:..' to match the name of directory
        self.append(self.ui.inflate('dialogging:main'))

        def post_item_bind(object, collection, item, ui):
            ui.find('btnStart').on('click', self.on_start, item)
            ui.find('btnRestart').on('click', self.on_restart, item)
            ui.find('btnStop').on('click', self.on_stop, item)
            ui.find('btnRemove').on('click', self.on_remove, item)
            #ui.find('btnSnapshot').on('click', self.on_snapshot, item)

            running = True if item.name in self.mgr.get_running() else False
            frozen = True if item.name in self.mgr.get_frozen() else False

            ui.find('btnStart').visible = not running
            ui.find('btnRestart').visible = running
            ui.find('btnStop').visible = running
            ui.find('btnFreeze').visible = not frozen
            ui.find('btnUnfreeze').visible = frozen

        self.find('collection').post_item_bind = post_item_bind

        self.obj_collection = self.mgr.get_all()

        self.binder = Binder(self, self)
        self.find('btnSnapshot').visible = False
        self.refresh()

    def refresh(self):
        self.populate()
        self.binder.update()

    def populate(self):
        self.obj_collection = self.mgr.get_all()
        for i in self.obj_collection:
            i._status = get(i.status)
            i._name = i.name
        self.binder.setup(self).populate()

    @on('btnCreateDialog', 'click')
    def on_apply(self):
        self.find('dialog').visible = True
        self.created = Instance()
        self.binder_d = Binder(self.created, self.find('dialog')).populate()

    @on('dialog', 'button')
    def on_close_dialog(self, button):
        self.find('dialog').visible = False
        if button == 'save':
            self.binder_d.update()
            self.created.name = self.created.name.replace(' ', '-')
            self.mgr.create(self.created.name)
        if self.mgr.exists(self.created.name):
            self.context.notify('info', "%s created" % self.created.name)
        self.refresh()

    def on_start(self, container):
        self.mgr.start(container.name.strip())
        self.populate()
        running = self.mgr.get_running()
        if container.name in running:
            self.context.notify('info',
                                "%s started successfully" % container.name)
        self.refresh()

    def on_stop(self, container):
        self.mgr.stop(container.name.strip())
        self.populate()
        stopped = self.mgr.get_stopped()
        if container.name in stopped:
            self.context.notify('info',
                                "%s stopped successfully" % container.name)
        self.refresh()

    def on_restart(self, container):
        self.mgr.restart(container.name.strip())
        self.populate()
        running = self.mgr.get_running()
        if container.name in running:
            self.context.notify('info',
                                "%s restarted successfully" % container.name)
        self.refresh()

    def on_remove(self, container):
        self.find('confirmDialog').visible = True
        self.conf = confirmDialog()
        self.conf.name = container.name.strip()
        self.conf.message = 'Really delete %s ?' % self.conf.name
        self.binder_r = Binder(self.conf,
                               self.find('confirmDialog')).populate()

    @on('confirmDialog', 'button')
    def on_remove_close(self, button):
        self.find('confirmDialog').visible = False
        if button == 'confirm':
            self.mgr.destroy(self.conf.name)
            self.populate()
            running = self.mgr.get_running()
            if self.conf.name not in running:
                self.context.notify('info',
                                    "%s removed successfully" % self.conf.name)
        self.refresh()
Пример #51
0
class LetsEncryptPlugin (SectionPlugin):
    pwd = os.path.join(os.path.dirname(os.path.realpath(__file__)), '')
    has_domains = False

    def init(self):
        self.title = 'LetsEncrypt'  # those are not class attributes and can be only set in or after init()
        self.icon = 'lock'
        self.category = 'Security'

        self.append(self.ui.inflate('letsencrypt:main'))
        
        self.settings = Settings()
        self.binder = Binder(self.settings, self)

        def delete_domain(domain, c):
            logging.debug('removed domain %s' % domain.name)
            c.remove(domain)
            logging.debug("domain info: %s %s" % (domain.subdomains, domain.name))
            self.save()
            
        def delete_cert(cert, c):
            logging.debug('removed cert %s' % cert.name)
            c.remove(cert)
            logging.debug("cert info: %s %s" % (cert.dir, cert.name))
            shutil.rmtree(cert.dir)

        def on_domain_bind(o, c, domain, u):
            domain.__old_name = domain.name

        def on_cert_bind(o, c, cert, u):
            cert.__old_name = cert.name

        def new_domain(c):
            name = 'domain.com'
            subdomains = 'www.domain.com\nblog.domain.com'
            return DomainsInfo(self, name, subdomains)
            
        self.find('certs').delete_item = delete_cert
        self.find('certs').post_item_bind = on_cert_bind
        self.find('domains').new_item = new_domain
        self.find('domains').delete_item = delete_domain
        self.find('domains').post_item_bind = on_domain_bind
     
    def fix_dependencies(self):
        if self.settings.dependencies_met is True:
            return
        
        changes = False
        
        # Install dehyrated (this is how we communicate with letsencrpyt)
        if not os.path.isfile(self.settings.basedir + self.settings.scriptname):
            self.context.notify('info', 'Fixing dehydrated dependency')
            changes = True
            
            if self.git_clone('https://github.com/lukas2511/dehydrated.git', self.settings.basedir):
                logging.debug("cloning of dehydrated successful")
                changes = False
        
        # Install nginxparser (this is how we edit nginx config files)
        installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()])
        
        if "nginxparser" not in str(installed_mods):
            self.context.notify('info', 'Fixing nginxparser dependency')
            changes = True
            
            if self.git_clone('https://github.com/fatiherikli/nginxparser', '~/'):
                logging.debug("cloning of nginxparser successful.. installing..")
                
                command = ['python', 'setup.py', 'install'];
                p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd='/root/nginxparser/')
                out, err = p.communicate()
                
                installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()])
                if "nginxparser" not in str(installed_mods):
                    logging.debug("Something went wrong with installing nginxparser..")
                else:
                    logging.debug("Installed nginxparser correctly.. importing modules..")
                    from nginxparser import load
                    from nginxparser import dumps 
                    changes = False
        
        if changes is False:
            self.settings.dependencies_met = True
            logging.debug("SUCCESSFULLY MET ALL DEPENDENCIES")
            
    def start_thread(self, target, *my_args, **my_keyword_args):
        thr = threading.Thread(target=target, args=my_args, kwargs=my_keyword_args)
        thr.daemon = True
        thr.start()
        return thr
        
    def on_page_load(self):
        self.refresh()
        
    def refresh(self):
        self.fix_dependencies()
        
        filepath = self.settings.basedir + self.settings.domainfile
        domains_file_read = ''
        
        if os.path.isfile(filepath):
            with open(filepath) as f:
                domains_file_read = f.readlines()
            domains_file_read = [x.strip() for x in domains_file_read]
            domains_file_read = "\n".join(domains_file_read)
        else:
            domains_file_read =  ""
       
        cron = self.check_cron()
        
        mysplit = domains_file_read.split("\n")
        tmp = []
        
        for lines in mysplit:
            mysecondsplit = lines.split(" ")
            main_domain = mysecondsplit.pop(0)
            sub_domains = ""
            
            if len(mysecondsplit) >= 1:
                sub_domains = mysecondsplit
                
            tmp.append(DomainsInfo(self, main_domain, "\n".join(sub_domains)))
            
        self.settings.domains = tmp
        self.find('cronjob').value = cron
  
        available_domains = self.list_available_certs()
        available_files = self.list_enabled_nginx_confs()
        temp_obj = []

        if available_domains:    
            for x in available_domains:
                #expiration date
                my_date = self.settings.basedir + '/certs/' + x + '/expiration.date'
                if os.path.isfile(my_date):
                    with open(my_date, 'r') as myfile:
                        my_date=myfile.read().replace('\n', '')
                else:
                    my_date = "NONE"
                
                #cert chain
                my_ssl_chain = self.settings.basedir + '/certs/' + x + '/chain.pem'
                if os.path.isfile(my_ssl_chain):
                    with open(my_ssl_chain, 'r') as myfile:
                        my_ssl_chain=myfile.read().replace('\n', '')
                else:
                    my_ssl_chain = "MISSING"
                
                #public key
                my_ssl_cert = self.settings.basedir + '/certs/' + x + '/cert.pem'
                if os.path.isfile(my_ssl_cert):
                    with open(my_ssl_cert, 'r') as myfile:
                        my_ssl_cert=myfile.read().replace('\n', '')
                else:
                    my_ssl_cert = "MISSING"
                    
                #public key and cert chain
                my_ssl_fullcert = self.settings.basedir + '/certs/' + x + '/fullchain.pem'
                if os.path.isfile(my_ssl_fullcert):
                    with open(my_ssl_fullcert, 'r') as myfile:
                        my_ssl_fullcert=myfile.read().replace('\n', '')
                else:
                    my_ssl_fullcert = "MISSING"
                    
                #private key
                my_ssl_key = self.settings.basedir + '/certs/' + x + '/privkey.pem'
                if os.path.isfile(my_ssl_key):
                    with open(my_ssl_key, 'r') as myfile:
                        my_ssl_key=myfile.read().replace('\n', '')
                else:
                    my_ssl_key = "MISSING"
                    
                temp_obj.append(CertificateInfo(self, self.settings.basedir + 'certs/' + x + '/', x, my_date, my_ssl_chain, my_ssl_cert, my_ssl_fullcert, my_ssl_key))
            
            self.settings.certs = temp_obj
        else:
            logging.debug("NO DOMAINS AVAILABLE")
            
        self.binder.setup(self.settings).populate()
    
    def list_enabled_nginx_confs(self):
        if not os.path.isdir(self.settings.nginx_sites_enabled):
            return False
            
        return [x for x in sorted(os.listdir(self.settings.nginx_sites_enabled)) if
                os.path.isfile(os.path.join(self.settings.nginx_sites_enabled, x))]

        
    def list_available_certs(self):
        if not os.path.isdir(self.settings.basedir + "certs"):
            return False
            
        return [x for x in sorted(os.listdir(self.settings.basedir + "certs")) if
                os.path.isdir(os.path.join(self.settings.basedir + "certs", x))]

    def write_domain_file(self):
        filepath = self.settings.basedir + self.settings.domainfile
        if len(self.settings.domains) < 1:
            self.context.notify('info', 'No domains specified')
            self.has_domains = False
            return
        
        final_str = ''

        for domains in self.settings.domains:
            final_str = final_str + domains.name + " "
            
            if domains.subdomains:
                tmp = domains.subdomains.split("\n")
                tmp = " ".join(tmp)
                final_str = final_str + tmp
            final_str = final_str + "\n"
            
        logging.debug("final string %s " % final_str)   
        
        file = open(filepath, 'w')
        if file.write(final_str) is None:
            self.has_domains = True
        else:
            self.context.notify('error', 'Domain file write error')
        file.close()

    def read_domain_file(self):
        filepath = self.settings.basedir + self.settings.domainfile
        if not open(filepath):
            self.context.notify('error', 'Domain file could not be read')

        file = open(filepath)
        with file as f:
            lines = f.readlines()
        return lines

    def create_folders(self):
        uid = pwd.getpwnam("www-data").pw_uid
        gid = grp.getgrnam("www-data").gr_gid

        if not os.path.exists(self.settings.basedir):
            os.makedirs(self.settings.basedir)
            os.chown(self.settings.basedir, uid, gid)
        if not os.path.exists(self.settings.wellknown):
            os.makedirs(self.settings.wellknown)
            os.chown(self.settings.wellknown, uid, gid)

    def create_dehydrated_config_file(self):
        template = """
        BASEDIR=$basedir
        WELLKNOWN=$wellknown
        """
        dict = {
            'basedir': self.settings.basedir,
            'wellknown': self.settings.wellknown
        }

        filepath = self.settings.basedir + self.settings.configname
        file = open(filepath, 'w')
        src = Template( template )
        if file.write(src.safe_substitute(dict)) is not None:
            self.context.notify('info', 'Letsencrypt error')
        file.close()
        
    def cleanup_oldfiles(self):
        filepath1 = self.settings.nginx_sites_enabled + self.settings.nginx_config
        filepath2 = self.settings.nginx_sites_available + self.settings.nginx_config
        filepath3 = self.settings.basedir + self.settings.configname
        filepath4 = self.settings.basedir + self.settings.domainfile
        
        if os.path.isfile(filepath1):
            os.remove(filepath1)
        if os.path.isfile(filepath2):
            os.remove(filepath2)
        if os.path.isfile(filepath3):
            os.remove(filepath3)
        if os.path.isfile(filepath4):
            os.remove(filepath4)
        
        self.context.notify('info', 'removed all local files')  
        
    def create_nginx_wellknown_config(self):
        if not self.check_nginx_dir_exists():
            self.context.notify('info', 'nginx_custom_dir() is not valid: %s')
            return False

        template = """
            server {
                server_name $domains;
                listen *:80;
                location $location {
                    alias $alias;
                }
            }
        """
        
        dict = {
            'location': '/.well-known/acme-challenge',
            'alias': self.settings.wellknown,
            'domains': " ".join(self.read_domain_file())
        }
        filepath = self.settings.nginx_sites_available + self.settings.nginx_config
        filepath2 = self.settings.nginx_sites_enabled + self.settings.nginx_config

        file = open(filepath, 'w')
        src = Template( template )
        if file.write(src.safe_substitute(dict)) is not None:
            self.context.notify('info', 'WELLKNOWN config write error')
        file.close()
        
        if os.path.isfile(filepath2):
            return True
            
        os.symlink(filepath, filepath2)

    def create_cron(self):
        file = open(self.settings.crontab_dir + self.settings.cronfile, 'w')
        template = "0 0 1 * * " + self.pwd + 'libs/letsencrypt.sh/letsencrypt.sh -c'
        if not file.write(template):
            self.context.notify('info', 'Cron job error')
        file.close()

    def remove_cron(self):
        if os.path.isfile(self.settings.crontab_dir + self.settings.cronfile):
            if os.remove(self.settings.crontab_dir + self.settings.cronfile):
                return True
            else:
                self.context.notify('info', 'Cron remove error')
                return False

    def check_cron(self):
        if os.path.isfile(self.settings.crontab_dir + self.settings.cronfile):
            return True
        return False

    # Update Nginx config files to use SSL
    def update_nginx_confs(self):
        available_domains = self.list_available_certs()
        filepath = self.settings.basedir + self.settings.domainfile
        domains = ''
        
        if os.path.isfile(filepath):
            with open(filepath) as f:
                domains = f.readlines()
            domains = [x.strip() for x in domains]
            domains = "\n".join(domains)
        
        mysplit = domains.split("\n")
        
        # Gets all enabled sites
        available_nginx_files = self.list_enabled_nginx_confs()

        for curr_domain in available_domains:
            for curr_nginx_file in available_nginx_files:
                logging.debug("## Use Certs: Checking config file - %s ##" % curr_nginx_file)
                
                if self.settings.nginx_config in curr_nginx_file:
                    logging.debug("Skipping file: %s" % curr_nginx_file)
                    continue
                    
                # Path is to the sites_available directory
                filepath = self.settings.nginx_sites_available + curr_nginx_file
                
                if not os.path.isfile(filepath):
                    logging.debug("File not in sites_available directory.. continuing")
                    continue
                    
                loaded_conf = load(open(filepath))
                
                for servers in loaded_conf:
                    server_conf = servers[1]
                    ssl_true = False
                    listen_position = None
                    server_name_position = None
                    ssl_cert_position = None
                    ssl_cert_key_position = None
                    curr_pos = 0
                    
                    for lines in server_conf:
                        if isinstance(lines[0], str):
                            
                            if 'server_name' in lines[0] and len(lines) > 1:
                                if server_name_position is not None:
                                    logging.debug("There must be more than one server_name_position.. skipping this file because we don't know how to handle it.")
                                    print("CONF %s" % loaded_conf)
                                    break
                                    
                                server_name_position = curr_pos
                                
                            if 'listen' in lines[0] and len(lines) > 1:
                                if listen_position is not None:
                                    listen_position = [listen_position]
                                    listen_position.append(curr_pos)
                                    print("There are multiple listen positions: %s" % listen_position)
                                else:    
                                    listen_position = curr_pos
                            
                            if 'ssl_certificate' in lines[0] and 'ssl_certificate_key' not in lines[0] and len(lines) > 1:
                                if ssl_cert_position is not None:
                                    logging.debug("There must be more than one ssl_certificate.. skipping this file because we don't know how to handle it.")
                                    print("CONF %s" % lines[0])
                                    break
                                    
                                ssl_cert_position = curr_pos
                            
                            if 'ssl_certificate_key' in lines[0] and len(lines) > 1:
                                if ssl_cert_key_position is not None:
                                    logging.debug("There must be more than one ssl_certificate_key.. skipping this file because we don't know how to handle it.")
                                    print("CONF %s" % lines[0])
                                    break
                                    
                                ssl_cert_key_position = curr_pos        
                                
                        curr_pos += 1
                    
                    if listen_position is not None and server_name_position is not None:
                        logging.debug("We have both listen and server name positions: listen position: %s | server_name_position: %s" % (listen_position, server_name_position))
                        
                        found_it = False
                        
                        for domain_list_line in mysplit:
                            if found_it is True:
                                break
                            
                            secondsplit = domain_list_line.split(" ")
                            
                            for domains in secondsplit:
                                logging.debug("CHECKING DOMAIN %s with: %s" % (server_conf[server_name_position][1], domains))
                                if server_conf[server_name_position][1] in domains:
                                    logging.debug("found our domain: %s in the list of domains" % server_conf[server_name_position][1])
                                    found_it = True
                                    break
                                
                        if found_it is False:
                            logging.debug("Server Config does not have any of our domains with SSL certs in the 'server_name' position.. skipping this config.")
                            continue
             
                        # Check if SSL is already setup
                        if ssl_cert_key_position is not None and ssl_cert_position is not None:

                            # Check if ports are setup too
                            if isinstance(listen_position, list):
                                already_setup = False
                                
                                for listens in listen_position:
                                    if "443" in server_conf[listens][1]:
                                        already_setup = True
                                        break
                                
                                if already_setup is True:
                                    logging.debug("This server is already setup for SSL")
                                    continue
                            
                            elif isinstance(listen_position, str):
                                if "443" in server_conf[listens][1]:
                                    continue
                                    
                        # Lets setup SSL now...
                        logging.debug("Attempting to setup SSL for domain: %s" % curr_domain)
                        
                        # Multiple listen calls..
                        if isinstance(listen_position, list):
                            already_setup = False
                                
                            for listens in listen_position:
                                if "443" in server_conf[listens][1]:
                                    already_setup = True
                                    break
                            
                            # Check if ssl port is already set
                            if already_setup is True:
                                logging.debug("Server port already setup for SSL")
                                
                            # 443 not set... set one of the listen calls to 443
                            else:
                                server_conf[listen_position[0]][1]
                                
                                # ssl port set already
                                if "443 ssl" in server_conf[listen_position[0]][1]:
                                    logging.debug("Server port already setup for SSL")
                                    
                                # ssl port not set yet..
                                else:   
                                    if ":" in server_conf[listen_position[0]][1]:
                                        tmp = server_conf[listen_position[0]][1].split(":")
                                        
                                        if tmp[1]:
                                            logging.debug("old port value %s" % server_conf[listen_position[0]][1])
                                            r = re.compile(r"\d{2,5}")
                                            tmp[1] = r.sub("443 ssl", tmp[1])
                                            
                                            server_conf[listen_position[0]][1] = ':'.join(tmp)
                                            logging.debug("new port value: %s" % server_conf[listen_position[0]][1])
                                    else:
                                        logging.debug("old port value: %s" % server_conf[listen_position[0]][1])
                                        r = re.compile(r"\d{2,5}")
                                        tmp = r.sub("443 ssl", server_conf[listen_position[0]][1])
                                        
                                        server_conf[listen_position[0]][1] = tmp
                                        logging.debug("new port value: %s" % server_conf[listen_position[0]][1]) 
                                        
                        # Single listen call..
                        else:
                            
                            # ssl port set already
                            if "443 ssl" in server_conf[listen_position][1]:
                                logging.debug("Server port already setup for SSL")
                                
                            # ssl port not set yet..
                            else:   
                                if ":" in server_conf[listen_position][1]:
                                    tmp = server_conf[listen_position][1].split(":")
                                    
                                    if tmp[1]:
                                        logging.debug("old port value %s" % server_conf[listen_position][1])
                                        r = re.compile(r"\d{2,5}")
                                        tmp[1] = r.sub("443 ssl", tmp[1])
                                        
                                        server_conf[listen_position][1] = ':'.join(tmp)
                                        logging.debug("new port value: %s" % server_conf[listen_position][1])
                                       
                                else:
                                    logging.debug("old port value: %s" % server_conf[listen_position][1])
                                    r = re.compile(r"\d{2,5}")
                                    tmp = r.sub("443 ssl", server_conf[listen_position][1])
                                    
                                    server_conf[listen_position][1] = tmp
                                    logging.debug("new port value: %s" % server_conf[listen_position][1])

                        cert_path = self.settings.basedir + '/certs/' + curr_domain + '/fullchain.pem'
                        cert_key_path = self.settings.basedir + '/certs/' + curr_domain + '/privkey.pem'

                        if ssl_cert_position is None:
                            server_conf.insert(0, ["ssl_certificate", cert_path])
                        else:
                            server_conf[ssl_cert_position][1] = cert_path
                            
                        if ssl_cert_key_position is None:
                            server_conf.insert(1, ["ssl_certificate_key", cert_key_path])
                        else:
                            server_conf[ssl_cert_key_position][1] = cert_key_path

                    file = open(filepath,"w") 
                    file.write(dumps(loaded_conf))
                    file.close()  
                    logging.debug("## FINISHED WITH SETTING UP NGINX WITH CERTS ##")
                    self.context.notify('info', 'Nginx is now using your valid certs.')
                    
    def check_nginx_dir_exists(self):
        if not os.path.exists(self.settings.nginx_sites_available):
            os.makedirs(self.settings.nginx_sites_available)
            
        if not os.path.exists(self.settings.nginx_sites_enabled):
            os.makedirs(self.settings.nginx_sites_enabled)

        if os.path.exists(self.settings.nginx_sites_available) and os.path.exists(self.settings.nginx_sites_enabled):
            return True
        
        self.context.notify('error', 'One or more nginx directories is incorrect')

    def request_certs_helper(self):
        self.save()
        time.sleep(5)
        
        if not os.path.exists(self.settings.basedir + "accounts"):
            self.context.notify('error', 'No accounts directory found, Registering before requesting certs..')
            self.request_certificates(True)
            
        if os.path.exists(self.settings.basedir + "accounts"):
            self.request_certificates(False)
            
    def request_certificates(self, register):
        filepath = self.settings.basedir + self.settings.scriptname
        params = [filepath, '-c']
        out = ""
        
        if self.find('renewal').value:
            params.append('--force')
            
        if register:
            params = [filepath, '--register', '--accept-terms']
        
        self.settings.output = "Disabling currently active SYMLINKS\n"
        self.binder.setup(self.settings).populate()
        
        available_files = self.list_enabled_nginx_confs()
        for curr_nginx_file in available_files:
            
            if self.settings.nginx_config in curr_nginx_file:
                logging.debug("Skipping file: %s" % curr_nginx_file)
                continue
            
            logging.debug("Disabling SYMLINK: %s" % curr_nginx_file)
            
            filepath = self.settings.nginx_sites_enabled + curr_nginx_file
            os.unlink(filepath)
        
        p = subprocess.Popen(params, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        success = False
        
        while p.poll() is None:
            output = p.stdout.readline()
            logging.debug("%s" % output)
            self.settings.output = output + self.settings.output
            self.binder.setup(self.settings).populate()
            
            if "Creating fullchain.pem" in output or "Skipping renew!" in output:
                success = True
  
        self.settings.output = "Re-enabling SYMLINKS...\n" + self.settings.output
        self.binder.setup(self.settings).populate()
        
        for curr_nginx_file in available_files:
            if not os.path.isfile(self.settings.nginx_sites_enabled + curr_nginx_file):
                filepath1 = self.settings.nginx_sites_available + curr_nginx_file
                filepath2 = self.settings.nginx_sites_enabled + curr_nginx_file
                os.symlink(filepath1, filepath2)    
        
        self.settings.output = "Restarting NGINX once more...\n" + self.settings.output
        self.binder.setup(self.settings).populate()
        self.restart_nginx()
        
        if success is True:
            self.settings.output = "Success creating certificates! Congratulations!\n" + self.settings.output
            self.binder.setup(self.settings).populate()
        else:
            self.settings.output = "Something went wrong. Read logs below.\n" + self.settings.output
            self.binder.setup(self.settings).populate()
            
    def restart_nginx(self):
        self.context.notify('info', 'Restarting Nginx')
        command = ['/usr/sbin/service', 'nginx', 'restart'];
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = p.communicate()

    def git_clone(self, repo, dir):
        logging.debug("Cloning REPO: %s into directory: %s" % (repo, dir))
        
        # GIT automatically makes directories
        command = ['/usr/bin/git', 'clone', repo, dir];
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = p.communicate()
        
        command = ['/usr/bin/git', 'remote', '-v'];
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir)
        out, err = p.communicate()
        
        if repo in out:
            return True
        else:
            return False
            
    def helper(self):        
        installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()])
        
        if "nginxparser" not in str(installed_mods):
            if self.git_clone('https://github.com/fatiherikli/nginxparser', '~/'):
                logging.debug("cloning of nginxparser successful.. installing..")
                
                command = ['python', 'setup.py', 'install'];
                p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd='/root/nginxparser/')
                out, err = p.communicate()
                
                installed_mods = sorted(["%s==%s" % (i.key, i.version) for i in pip.get_installed_distributions()])
                if "nginxparser" not in str(installed_mods):
                    logging.debug("Something went wrong with installing nginxparser..")
            
    def save(self):
        self.binder.update()
        self.binder.populate()
        self.create_folders()
        self.write_domain_file()

        if not self.has_domains:
            
            self.cleanup_oldfiles()
            self.restart_nginx()
            return

        self.create_dehydrated_config_file()
        self.create_nginx_wellknown_config()

        if self.settings.cronjob:
            self.create_cron()
        else:
            self.remove_cron()
        self.restart_nginx()

    @on('done', 'click')
    def on_output_done(self):
        self.find('hidden_output').visible = False
        
    @on('tabs', 'switch')
    def tab_switched(self):
        self.refresh()
        
    @on('save', 'click')
    def save_button(self):
        self.save()       

    @on('updatenginx', 'click')
    def updatenginx_button(self):     
        self.update_nginx_confs()
        self.restart_nginx()
    
    @on('helperbutton', 'click')
    def helper_button(self):
        self.helper()

    @on('request', 'click')
    def request_button(self):
        self.settings.output = "Saving configuration... Waiting 5 seconds..."
        self.binder.setup(self.settings).populate()
        self.find('hidden_output').visible = True
        self.start_thread(self.request_certs_helper)
Пример #52
0
class MySQLExtension(BaseExtension):
    default_config = {
        'databases': [],
        'users': [],
    }
    name = 'MySQL'

    def init(self):
        self.append(self.ui.inflate('vh-mysql:ext'))
        self.binder = Binder(self, self)
        self.refresh()
        self.db = MySQLDB.get()

    @staticmethod
    def selftest():
        try:
            MySQLDB.get().query_databases()
        except:
            pass

    def refresh(self):
        if not 'databases' in self.config:
            if self.config['created']:
                self.config['databases'] = [{
                    'name': self.config['name'],
                }]
                self.config['users'] = [{
                    'name': self.config['user'],
                    'password': self.config['password'],
                }]
            else:
                self.config['databases'] = []
                self.config['users'] = []

            del self.config['created']
            del self.config['name']
            del self.config['user']
            del self.config['password']

        def post_db_bind(object, collection, item, ui):
            ui.find('detach').on('click', self.on_detach_db, item)

        self.find('databases').post_item_bind = post_db_bind

        def post_user_bind(object, collection, item, ui):
            ui.find('detach').on('click', self.on_detach_user, item)

        self.find('users').post_item_bind = post_user_bind

        self.binder.setup().populate()
        self.find('db-name').value = self.website.slug
        self.find('db-username').value = self.website.slug

        self.find('db-password').value = str(uuid.uuid4())

        self.find('databases').delete_item = lambda i, c: self.on_delete_db(i)
        self.find('users').delete_item = lambda i, c: self.on_delete_user(i)

    def update(self):
        self.binder.update()

    def on_destroy(self):
        pass

    @on('create-db', 'click')
    def on_create_db(self):
        try:
            self.db.query_databases()
        except Exception, e:
            self.context.notify('error', str(e))
            self.context.launch('configure-plugin', plugin=self.db)
            return

        dbname = self.find('db-name').value

        for db in self.db.query_databases():
            if db.name == dbname:
                self.context.notify('error',
                                    _('This database name is already used'))
                return

        db_cfg = {'name': dbname}

        try:
            self.db.query_create(db_cfg['name'])
        except Exception, e:
            self.context.notify('error', str(e))
            return
Пример #53
0
class Packages(SectionPlugin):
    platforms = ['debian', 'centos', 'freebsd', 'arch', 'mageia']

    def init(self):
        self.title = _('Packages')
        self.icon = 'gift'
        self.category = _('System')

        self.mgr = PackageManager.get()

        self.append(self.ui.inflate('packages:main'))

        def post_item_bind(object, collection, item, ui):
            ui.find('install').on('click', self.on_install, item)
            ui.find('remove').on('click', self.on_remove, item)
            ui.find('cancel').on('click', self.on_cancel, item)
            ui.find('install').visible = item.action is None
            ui.find(
                'remove').visible = item.action is None and item.state == 'i'
            ui.find('cancel').visible = item.action is not None

        self.find('upgradeable').post_item_bind = post_item_bind
        self.find('search').post_item_bind = post_item_bind
        self.find('pending').post_item_bind = post_item_bind

        self.binder = Binder(None, self.find('bind-root'))
        self.binder_p = Binder(self, self.find('bind-pending'))
        self.binder_s = CollectionAutoBinding([], None,
                                              self.find('search')).populate()

        self.pending = {}
        self.installation_running = False
        self.action_queue = []

    def refresh(self):
        self.fill(self.mgr.upgradeable)
        self.binder.setup(self.mgr).populate()
        self.binder_s.unpopulate().populate()
        self._pending = self.pending.values()
        self.binder_p.setup(self).populate()

    def run(self, tasks):
        if self.installation_running:
            self.action_queue += tasks
            self.context.notify('info', _('Enqueueing package installation'))
            return

        self.installation_running = True

        def callback():
            self.installation_running = False
            if self.action_queue:
                self.run(self.action_queue)
                self.action_queue = []
                return
            self.context.notify('info', _('Installation complete!'))

        self.mgr.do(tasks, callback=callback)

    @intent('install-package')
    @restrict('packages:modify')
    def intent_install(self, package):
        p = PackageInfo()
        p.name, p.action = package, 'i'
        self.run([p])

    def on_page_load(self):
        self.context.endpoint.send_progress(_('Querying package manager'))
        self.mgr.refresh()
        self.refresh()

    @restrict('packages:modify')
    def on_install(self, package):
        package.action = 'i'
        self.pending[package.name] = package
        self.refresh()

    @restrict('packages:modify')
    def on_cancel(self, package):
        package.action = None
        if package.name in self.pending:
            del self.pending[package.name]
        self.refresh()

    @restrict('packages:modify')
    def on_remove(self, package):
        package.action = 'r'
        self.pending[package.name] = package
        self.refresh()

    @on('get-lists-button', 'click')
    @restrict('packages:refresh')
    def on_get_lists(self):
        self.mgr.get_lists()

    @on('apply-button', 'click')
    @restrict('packages:modify')
    def on_apply(self):
        self.run(self.pending.values())
        self.pending = {}
        self.refresh()

    @on('upgrade-all-button', 'click')
    @restrict('packages:modify')
    def on_upgrade_all(self):
        for p in self.mgr.upgradeable:
            p.action = 'i'
            self.pending[p.name] = p
        self.refresh()

    @on('cancel-all-button', 'click')
    @restrict('packages:modify')
    def on_cancel_all(self):
        self.pending = {}
        self.refresh()

    def fill(self, packages):
        for p in packages:
            if p.name in self.pending:
                p.action = self.pending[p.name].action

    @on('search-button', 'click')
    def on_search(self):
        query = self.find('search-box').value
        results = self.mgr.search(query)
        if self.binder_s:
            self.binder_s.unpopulate()
        if len(results) > 100:
            self.find('search-counter'
                      ).text = _('%i found, 100 shown') % len(results)
            results = results[:100]
        else:
            self.find('search-counter').text = _('%i found') % len(results)

        self.fill(results)
        self.binder_s = CollectionAutoBinding(results, None,
                                              self.find('search')).populate()
Пример #54
0
class OpenVPN (SectionPlugin):
    def init(self):
        self.title = _('OpenVPN')
        self.icon = 'globe'
        self.category = _('Software')

        self.append(self.ui.inflate('openvpn:main'))

        def disconnect(u, c):
            try:
                self.backend.killbyaddr(u.raddress)
                time.sleep(1)
            except Exception as e:
                self.context.notify('error', e.message)
            self.refresh()

        self.find('clients').delete_item = disconnect

        self.binder = Binder(None, self)
        self.backend = OpenVPNBackend.get()

    def on_page_load(self):
        self.refresh()

    @on('hard-restart', 'click')
    def on_hard_restart(self):
        self.backend.restarthard()
        time.sleep(2)

    @on('soft-restart', 'click')
    def on_soft_restart(self):
        self.backend.restartcond()
        time.sleep(2)

    def refresh(self):
        try:
            self.backend.setup()
        except Exception as e:
            self.context.notify('error', e.message)
            self.context.launch('configure-plugin', plugin=self.backend)
            return

        self.state = State()

        try:
            self.backend.connect()
            self.state.stats = self.backend.getstats()
            self.state.status = self.backend.getstatus()
            self.state.messages = []
            self.state.clients = []
            for d in self.state.status['clients']:
                c = Client()
                c.__dict__.update(d)
                self.state.clients.append(c)
            for d in self.backend.getmessages():
                m = Message()
                m.timestamp, m.flags, m.text = d[:3]
                self.state.messages.append(m)

        except Exception as e:
            self.context.notify('error', e.message)

        self.binder.setup(self.state).populate()
Пример #55
0
class Firewall(SectionPlugin):
    platforms = ['centos', 'debian', 'arch', 'mageia']
    manager_class = FirewallManager

    def init(self):
        self.title = _('Firewall')
        self.icon = 'fire'
        self.category = _('System')

        self.append(self.ui.inflate('iptables:main'))

        self.fw_mgr = self.manager_class.get()
        self.config = IPTablesConfig(path=self.fw_mgr.config_path_ajenti)
        self.binder = Binder(None, self.find('config'))

        self.find('tables').new_item = lambda c: TableData()
        self.find('chains').new_item = lambda c: ChainData()
        self.find('rules').new_item = lambda c: RuleData()
        self.find('options').new_item = lambda c: OptionData()
        self.find('options').binding = OptionsBinding
        self.find('options').filter = lambda i: not i.name in ['j', 'jump']

        def post_rule_bind(o, c, i, u):
            u.find('add-option').on('change', self.on_add_option, c, i, u)
            action = ''
            j_option = i.get_option('j', 'jump')
            if j_option:
                action = j_option.arguments[0].value
            u.find('action').text = action
            u.find('action').style = 'iptables-action iptables-%s' % action
            u.find('action-select').value = action
            u.find('title').text = i.comment if i.comment else i.summary

        def post_rule_update(o, c, i, u):
            action = u.find('action-select').value
            j_option = i.get_option('j', 'jump')
            if j_option:
                j_option.arguments[0].value = action
            else:
                if action:
                    o = OptionData.create_destination()
                    o.arguments[0].value = action
                    i.options.append(o)

        self.find('rules').post_item_bind = post_rule_bind
        self.find('rules').post_item_update = post_rule_update

        self.find('add-option').values = self.find('add-option').labels = [
            _('Add option')
        ] + sorted(OptionData.templates.keys())

    def on_page_load(self):
        if not os.path.exists(self.fw_mgr.config_path_ajenti):
            if not os.path.exists(self.fw_mgr.config_path):
                open(self.fw_mgr.config_path, 'w').write("""
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 8000 -j ACCEPT
COMMIT
                """)
            open(self.fw_mgr.config_path_ajenti,
                 'w').write(open(self.fw_mgr.config_path).read())
        self.config.load()
        self.refresh()

    @on('load-current', 'click')
    def on_load_current(self):
        subprocess.call(
            '%s > %s' %
            (self.fw_mgr.iptables_save_binary, self.fw_mgr.config_path_ajenti),
            shell=True)
        self.config.load()
        self.refresh()

    def refresh(self):
        self.find('autostart').text = (_('Disable')
                                       if self.fw_mgr.get_autostart_state()
                                       else _('Enable')) + _(' autostart')

        actions = ['ACCEPT', 'DROP', 'REJECT', 'LOG', 'MASQUERADE', 'DNAT', 'SNAT'] + \
            list(set(itertools.chain.from_iterable([[c.name for c in t.chains] for t in self.config.tree.tables])))
        self.find('action-select').labels = actions
        self.find('action-select').values = actions
        self.find('chain-action-select').labels = actions
        self.find('chain-action-select').values = actions
        self.binder.setup(self.config.tree).populate()

    @on('autostart', 'click')
    def on_autostart_change(self):
        self.fw_mgr.set_autostart_state(not self.fw_mgr.get_autostart_state())
        self.refresh()

    def on_add_option(self, options, rule, ui):
        self.binder.update()
        o = OptionData.create(ui.find('add-option').value)
        ui.find('add-option').value = ''
        rule.options.append(o)
        self.binder.populate()

    @on('save', 'click')
    def save(self):
        self.binder.update()

        for t in self.config.tree.tables:
            for c in t.chains:
                for r in c.rules:
                    r.verify()

        self.config.save()

        open(self.fw_mgr.config_path, 'w').write(''.join(
            l.split('#')[0] + '\n'
            for l in open(self.fw_mgr.config_path_ajenti).read().splitlines()))
        self.refresh()
        self.context.notify('info', _('Saved'))

    @on('edit', 'click')
    def raw_edit(self):
        self.context.launch('notepad', path=self.fw_mgr.config_path_ajenti)

    @on('apply', 'click')
    def apply(self):
        self.save()
        cmd = 'cat %s | %s' % (self.fw_mgr.config_path,
                               self.fw_mgr.iptables_restore_binary)
        if subprocess.call(cmd, shell=True) != 0:
            self.context.launch('terminal', command=cmd)
        else:
            self.context.notify('info', _('Applied successfully'))
Пример #56
0
class fail2ban(SectionPlugin):
    def init(self):
        self.title = 'fail2ban'
        self.icon = 'shield'
        self.category = _('Software')
        self.f2b_v = subprocess.check_output(['fail2ban-client', '--version']).splitlines()[0]
        self.append(self.ui.inflate('fail2ban:main'))
        self.binder = Binder(self, self)
        self.update_status = True

        def on_config_update(o, c, config, u):
            if config.__old_name != config.name:
                if os.path.exists(os.path.join(c.path, config.name)):
                    self.context.notify('error', _(
                        'File with name {0} already exists in {1}.').format(config.name, c.path))
                    self.update_status = False
                    config.name = config.__old_name
                    return
                logging.debug('renamed config file %s to %s' % (config.__old_name, config.name))
                os.unlink(config.configfile)
            config.save()
            self.update_status = True

        def on_config_bind(o, c, config, u):
            config.__old_name = config.name

        def new_config(c):
            new_fn = 'untitled{0}.conf'
            s_fn = new_fn.format('')
            filename = os.path.join(c.path, s_fn)
            i = 1
            while os.path.exists(filename):
                s_fn = new_fn.format('_' + str(i))
                filename = os.path.join(c.path, s_fn)
                i += 1
            try:
                logging.debug('add config %s' % filename)
                conf = f2b_Config(s_fn, c.path)
                conf.config = config_templates[c.type]
                return conf.save()
            except IOError as e:
                print('Error writing file {0} in {1}'.format(filename, c.path))

        def delete_config(config, c):
            filename = config.configfile
            logging.debug('removed config %s' % config.configfile)
            c.remove(config)
            os.unlink(filename)

        self.find('configlist').post_item_update = on_config_update
        self.find('configlist').post_item_bind = on_config_bind
        self.find('configlist').new_item = new_config
        self.find('configlist').delete_item = delete_config

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.configurations = [
            f2b_Configs(k, d).update()
            for k, d in config_dirs.iteritems()
            if os.path.isdir(d)
        ]
        self.binder.setup(self).populate()

    @on('save-button', 'click')
    def save(self):
        self.binder.update()
        if self.update_status:
            self.context.notify('info', _('Saved'))
        self.update_status = True
        self.binder.setup(self).populate()

    @on('check-regex', 'click')
    def check_regex(self):
        self.find('check-status').text = ''
        log_fname = self.find('log-filename').value
        filter_fname = self.find('filter-filename').value
        log_as_text = self.find('log-file').value
        filter_as_text = self.find('filter-file').value
        if not (log_fname or log_as_text) or not (filter_fname or filter_as_text):
            logging.debug('Filter checker. Some parameters are empty.')
            self.context.notify('error', _('Some parameters are empty.'))
            return

        with open(filter_fname + '.tmp', 'w') as rt:
            rt.write(self.find('filter-file').value)
            rt_name = rt.name
            rt.close()

        p = subprocess.Popen(['fail2ban-regex', '--full-traceback', log_fname, rt_name], stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        out, err = p.communicate()
        self.find('check-status').text = out

        os.unlink(rt_name)

    @on('open-filter-button', 'click')
    def open_filter(self):
        self.find('openfilterdialog').show()

    @on('openfilterdialog', 'button')
    def on_open_filter_dialog(self, button):
        self.find('openfilterdialog').visible = False

    @on('openfilterdialog', 'select')
    def on_filter_file_select(self, path=None):
        if not path:
            return
        self.find('openfilterdialog').visible = False
        self.find('filter-filename').value = path
        self.find('filter-file').value = ''.join(open(path).readlines())

    @on('open-log-button', 'click')
    def open_log(self):
        self.find('openlogdialog').show()

    @on('openlogdialog', 'button')
    def on_open_log_dialog(self, button):
        self.find('openlogdialog').visible = False

    @on('openlogdialog', 'select')
    def on_log_file_select(self, path=None):
        if not path:
            return
        self.find('openlogdialog').visible = False
        self.find('log-filename').value = path
        self.find('log-file').value = ''.join(open(path).readlines())

    @on('save-filter-button', 'click')
    def save_filter(self):
        try:
            open(self.find('filter-filename').value, 'w').write(self.find('filter-file').value)
        except IOError as e:
            self.context.notify('error', _('Could not save config file. %s') % str(e))
            logging.error(e.message)
Пример #57
0
class DBPlugin(SectionPlugin):
    service_name = ''
    service_buttons = []
    has_users = True

    def init(self):
        self.append(self.ui.inflate('db_common:main'))
        self.binder = Binder(None, self)
        self.find_type('servicebar').buttons = self.service_buttons

        def delete_db(db, c):
            self.query_drop(db)
            self.refresh()

        self.find('databases').delete_item = delete_db

        def delete_user(user, c):
            self.query_drop_user(user)
            self.refresh()

        self.find('users').delete_item = delete_user

    def on_page_load(self):
        self.refresh()

    @on('sql-run', 'click')
    def on_sql_run(self):
        try:
            result = self.query_sql(
                self.find('sql-db').value,
                self.find('sql-input').value)
            self.context.notify('info', _('Query finished'))
        except Exception as e:
            self.context.notify('error', str(e))
            return

        tbl = self.find('sql-output')
        tbl.empty()

        if len(result) > 200:
            self.context.notify(
                'info',
                _('Output cut from %i rows to 200') % len(result))
            result = result[:200]

        for row in result:
            erow = self.ui.create('dtr')
            tbl.append(erow)
            for cell in row:
                ecell = self.ui.create('dtd')
                ecell.append(self.ui.create('label', text=str(cell)))
                erow.append(ecell)

    @on('add-db', 'click')
    def on_add_db(self):
        self.find('db-name-dialog').value = ''
        self.find('db-name-dialog').visible = True

    @on('add-user', 'click')
    def on_add_user(self):
        self.find('add-user-dialog').visible = True

    def refresh(self):
        self.binder.setup(self).populate()
        self.databases = []
        self.users = []
        try:
            self.databases = self.query_databases()
            if self.has_users:
                self.users = self.query_users()
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.context.notify('error', str(e))
            if hasattr(self, 'config_class'):
                self.context.launch('configure-plugin',
                                    plugin=self.config_class.get())
            return

        self.binder.unpopulate()
        self.find('sql-db').labels = self.find('sql-db').values = [
            x.name for x in self.databases
        ]
        self.binder.populate()
        self.find_type('servicebar').reload()

    @on('db-name-dialog', 'submit')
    def on_db_name_dialog_submit(self, value=None):
        try:
            self.query_create(value)
        except Exception as e:
            self.context.notify('error', str(e))
            return
        self.refresh()

    @on('add-user-dialog', 'button')
    def on_add_user_dialog(self, button=None):
        d = self.find('add-user-dialog')
        d.visible = False
        if button == 'ok':
            u = User()
            u.name = d.find('name').value
            u.host = d.find('host').value
            u.password = d.find('password').value
            try:
                self.query_create_user(u)
            except Exception as e:
                self.context.notify('error', str(e))
                return

        self.refresh()

    def query_sql(self, db, sql):
        raise NotImplementedError()

    def query_databases(self):
        raise NotImplementedError()

    def query_drop(self, db):
        raise NotImplementedError()

    def query_create(self, name):
        raise NotImplementedError()

    def query_users(self):
        raise NotImplementedError()

    def query_create_user(self, user):
        raise NotImplementedError()

    def query_drop_user(self, user):
        raise NotImplementedError()
Пример #58
0
class Tasks(SectionPlugin):
    def init(self):
        self.title = _('Tasks')
        self.icon = 'cog'
        self.category = _('Tools')

        self.append(self.ui.inflate('tasks:main'))

        self.manager = TaskManager.get(manager.context)
        self.binder = Binder(None, self)

        def post_td_bind(object, collection, item, ui):
            if item.get_class():
                params_ui = self.ui.inflate(item.get_class().ui)
                item.binder = DictAutoBinding(item, 'params',
                                              params_ui.find('bind'))
                item.binder.populate()
                ui.find('slot').empty()
                ui.find('slot').append(params_ui)

        def post_td_update(object, collection, item, ui):
            if hasattr(item, 'binder'):
                item.binder.update()

        def post_rt_bind(object, collection, item, ui):
            def abort():
                item.abort()
                self.refresh()

            ui.find('abort').on('click', abort)

        self.find('task_definitions').post_item_bind = post_td_bind
        self.find('task_definitions').post_item_update = post_td_update
        self.find('running_tasks').post_item_bind = post_rt_bind

        self.find('job_definitions').new_item = lambda c: JobDefinition()

    def on_page_load(self):
        self.refresh()

    @on('refresh', 'click')
    def refresh(self):
        self.manager.refresh()

        self.binder.unpopulate()

        dd = self.find('task-classes')
        dd.labels = []
        dd.values = []
        for task in Task.get_classes():
            if not task.hidden:
                dd.labels.append(task.name)
                dd.values.append(task.classname)

        dd = self.find('task-selector')
        dd.labels = [_.name for _ in self.manager.task_definitions]
        dd.values = [_.id for _ in self.manager.task_definitions]
        self.find('run-task-selector').labels = dd.labels
        self.find('run-task-selector').values = dd.values

        self.binder.setup(self.manager).populate()

    @on('run-task', 'click')
    def on_run_task(self):
        self.manager.run(task_id=self.find('run-task-selector').value,
                         context=self.context)
        self.refresh()

    @on('create-task', 'click')
    def on_create_task(self):
        cls = self.find('task-classes').value
        td = TaskDefinition(task_class=cls)
        td.name = td.get_class().name

        self.manager.task_definitions.append(td)
        self.refresh()

    @on('save', 'click')
    def on_save(self):
        self.binder.update()
        self.manager.save()
        self.refresh()
Пример #59
0
class CSFSection (SectionPlugin):
    def init(self):
        self.title = _('CSF Firewall')
        self.icon = 'fire'
        self.category = _('System')
        self.backend = CSFBackend.get()

        self.append(self.ui.inflate('csf:main'))

        self.config = CSFConfig(path='/etc/csf/csf.conf')
        self.list_allow = []
        self.list_deny = []
        self.list_tempallow = []
        self.list_tempban = []

        def delete_rule(csf_option, i):
            self.save()
            subprocess.call(['csf', csf_option, i.value.split('#')[0]])
            self.refresh()

        self.find('list_allow').on_delete = lambda i, c: delete('-ar', i)
        self.find('list_deny').on_delete = lambda i, c: delete('-dr', i)
        self.find('list_tempallow').on_delete = lambda i, c: delete('-tr', i)
        self.find('list_tempban').on_delete = lambda i, c: delete('-tr', i)

        def add_rule(csf_option, address):
            self.save()
            p = subprocess.Popen(['csf', csf_option, address], stdout=subprocess.PIPE)
            o, e = p.communicate()
            self.context.notify('info', o)
            self.refresh()

        self.find('list_allow-add').on('click', lambda: add_rule('-a', self.find('permanent-lists-add-address').value))
        self.find('list_deny-add').on('click', lambda: add_rule('-d', self.find('permanent-lists-add-address').value))
        self.find('list_tempallow-add').on('click', lambda: add_rule('-ta', self.find('temporary-lists-add-address').value))
        self.find('list_tempban-add').on('click', lambda: add_rule('-td', self.find('temporary-lists-add-address').value))

        self.binder = Binder(None, self)
        self.binder_lists = Binder(self, self.find('lists'))

    def on_page_load(self):
        self.refresh()

    def refresh(self):
        self.config.load()
        self.list_allow = self.backend.read_list('allow')
        self.list_deny = self.backend.read_list('deny')
        self.list_tempallow = self.backend.read_list('tempallow')
        self.list_tempban = self.backend.read_list('tempban')
        self.binder.setup(self.config.tree).populate()
        self.binder_lists.populate()

    @on('apply', 'click')
    def on_apply(self):
        self.backend.apply()
        self.context.notify('info', _('Applied'))

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.config.save()
        self.backend.write_list('allow', self.list_allow)
        self.backend.write_list('deny', self.list_deny)
        self.backend.write_list('tempallow', self.list_tempallow)
        self.backend.write_list('tempban', self.list_tempban)

        self.binder.setup(self.config.tree).populate()
        self.context.notify('info', _('Saved'))
        try:
            self.backend.test_config()
            self.context.notify('info', _('Self-test OK'))
        except Exception as e:
            self.context.notify('error', str(e))
Пример #60
0
class Filesystems(SectionPlugin):
    def init(self):
        self.title = _('Filesystems')
        self.icon = 'hdd'
        self.category = _('System')
        self.append(self.ui.inflate('fstab:main'))

        self.find('type').labels = [
            'Auto', 'EXT2', 'EXT3', 'EXT4', 'NTFS', 'FAT', 'ZFS', 'ReiserFS',
            'Samba', 'None', 'Loop'
        ]
        self.find('type').values = [
            'auto', 'ext2', 'ext3', 'ext4', 'ntfs', 'vfat', 'zfs', 'reiser',
            'smb', 'none', 'loop'
        ]

        self.fstab_config = FSTabConfig(path='/etc/fstab')
        self.mounts = MountsBackend.get()

        self.binder = Binder(None, self)
        self.find('fstab').find(
            'filesystems').new_item = lambda c: FilesystemData()

        def post_mount_bind(object, collection, item, ui):
            ui.find('umount').on('click', self.on_umount, item)

        self.find('mounts').find(
            'filesystems').post_item_bind = post_mount_bind

    def on_page_load(self):
        self.refresh()

    def on_umount(self, mount):
        subprocess.call(['umount', mount.mountpoint])
        self.context.notify('info', _('Unmounted'))
        self.refresh()

    @on('mount-all', 'click')
    def on_mount_all(self):
        self.save()
        if subprocess.call(['mount', '-a']):
            self.context.notify('error', _('mount -a failed'))
        self.refresh()

    @on('refresh', 'click')
    def refresh(self):
        self.binder.unpopulate()
        self.reload_disks()
        self.fstab_config.load()
        self.fstab = self.fstab_config.tree
        self.mounts.reload()
        self.binder.setup(self).populate()

    def reload_disks(self):
        lst = disks.list_devices(by_uuid=True, by_id=True, by_label=True)
        self.find('device').labels = [x[0] for x in lst]
        self.find('device').values = [x[1] for x in lst]

    @on('save', 'click')
    def save(self):
        self.binder.update()
        self.fstab_config.save()
        self.context.notify('info', _('Saved'))