From 01e50d592e72d6c4bb15df3f8faca2dfbc127774 Mon Sep 17 00:00:00 2001
From: ibuler <ibuler@qq.com>
Date: Sun, 5 Mar 2017 11:38:02 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90AdHoc=20JMSHost=20JMSInventor?=
 =?UTF-8?q?y?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 apps/assets/models/asset.py   |  12 +
 apps/assets/models/user.py    |  21 +-
 apps/ops/api/serializers.py   |  61 ----
 apps/ops/api/views.py         |  72 -----
 apps/ops/models/__init__.py   |   2 -
 apps/ops/models/cron.py       |  61 ----
 apps/ops/models/sudo.py       | 321 ---------------------
 apps/ops/models/utils.py      |   5 +-
 apps/ops/urls/api_urls.py     |  16 +-
 apps/ops/urls/view_urls.py    |  12 -
 apps/ops/utils/ansible_api.py | 505 ++++++++++++++++++----------------
 apps/ops/views.py             |  47 +---
 12 files changed, 301 insertions(+), 834 deletions(-)
 delete mode 100644 apps/ops/models/cron.py
 delete mode 100644 apps/ops/models/sudo.py

diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py
index da1ee37d6..4f9162de4 100644
--- a/apps/assets/models/asset.py
+++ b/apps/assets/models/asset.py
@@ -87,6 +87,18 @@ class Asset(models.Model):
     def to_json(self):
         pass
 
+    def _to_secret_json(self):
+        """Ansible use it create inventory"""
+        return {
+            'hostname': self.hostname,
+            'ip': self.ip,
+            'port': self.port,
+            'groups': [group.name for group in self.groups.all()],
+            'username': self.admin_user.username,
+            'password': self.admin_user.password,
+            'private_key': self.admin_user.private_key,
+        }
+
     class Meta:
         unique_together = ('ip', 'port')
 
diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py
index 5440d3910..94230ad9d 100644
--- a/apps/assets/models/user.py
+++ b/apps/assets/models/user.py
@@ -9,7 +9,7 @@ import logging
 from django.utils.translation import ugettext_lazy as _
 from django.core.exceptions import ValidationError
 
-from common.utils import signer, validate_ssh_private_key
+from common.utils import signer, validate_ssh_private_key, ssh_key_string_to_obj
 
 __all__ = ['AdminUser', 'SystemUser', 'private_key_validator']
 logger = logging.getLogger(__name__)
@@ -24,12 +24,20 @@ def private_key_validator(value):
 
 
 class AdminUser(models.Model):
+    BECOME_METHOD_CHOICES = (
+        ('sudo', 'sudo'),
+        ('su', 'su'),
+    )
     name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
     username = models.CharField(max_length=16, verbose_name=_('Username'))
     _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
     _private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
                                     validators=[private_key_validator,])
     _public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
+    become = models.BooleanField(default=True)
+    become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
+    become_user = models.CharField(default='root', max_length=64)
+    become_password = models.CharField(default='', max_length=128)
     comment = models.TextField(blank=True, verbose_name=_('Comment'))
     date_created = models.DateTimeField(auto_now_add=True, null=True)
     created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
@@ -41,7 +49,10 @@ class AdminUser(models.Model):
 
     @property
     def password(self):
-        return signer.unsign(self._password)
+        if self._password:
+            return signer.unsign(self._password)
+        else:
+            return ''
 
     @password.setter
     def password(self, password_raw):
@@ -49,7 +60,11 @@ class AdminUser(models.Model):
 
     @property
     def private_key(self):
-        return signer.unsign(self._private_key)
+        if self._private_key:
+            key_str = signer.unsign(self._private_key)
+            return ssh_key_string_to_obj(key_str)
+        else:
+            return None
 
     @private_key.setter
     def private_key(self, private_key_raw):
diff --git a/apps/ops/api/serializers.py b/apps/ops/api/serializers.py
index 24abe7e2a..af472b3cb 100644
--- a/apps/ops/api/serializers.py
+++ b/apps/ops/api/serializers.py
@@ -1,65 +1,4 @@
 # ~*~ coding: utf-8 ~*~
 from __future__ import unicode_literals
 
-from ops.models import *
-from rest_framework import serializers
 
-
-class HostAliaSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = HostAlia
-
-
-class CmdAliaSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = CmdAlia
-
-
-class UserAliaSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = UserAlia
-
-
-class RunasAliaSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = RunasAlia
-
-
-class ExtraconfSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = Extra_conf
-
-
-class PrivilegeSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = Privilege
-
-
-class SudoSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = Sudo
-
-
-class CronTableSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = CronTable
-
-class TaskSerializer(serializers.ModelSerializer):
-    sub_tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
-
-    class Meta:
-        model = Task
-        read_only_fields = ('record',)
-
-class SubTaskSerializer(serializers.ModelSerializer):
-
-    class Meta:
-        model = SubTask
diff --git a/apps/ops/api/views.py b/apps/ops/api/views.py
index eaacb2b76..affa9cdc7 100644
--- a/apps/ops/api/views.py
+++ b/apps/ops/api/views.py
@@ -5,75 +5,3 @@ from rest_framework import viewsets
 from serializers import *
 from permissions import *
 
-__all__ = ["HostAliaViewSet",
-           "CmdAliaViewSet",
-           "UserAliaViewSet",
-           "RunasAliaViewSet",
-           "ExtraconfViewSet",
-           "PrivilegeViewSet",
-           "SudoViewSet",
-           "CronTableViewSet",
-           "TaskViewSet",
-           "SubTaskViewSet",
-           ]
-
-
-class HostAliaViewSet(viewsets.ModelViewSet):
-    queryset = HostAlia.objects.all()
-    serializer_class = HostAliaSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class CmdAliaViewSet(viewsets.ModelViewSet):
-    queryset = CmdAlia.objects.all()
-    serializer_class = CmdAliaSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class UserAliaViewSet(viewsets.ModelViewSet):
-    queryset = UserAlia.objects.all()
-    serializer_class = UserAliaSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class RunasAliaViewSet(viewsets.ModelViewSet):
-    queryset = RunasAlia.objects.all()
-    serializer_class = RunasAliaSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class ExtraconfViewSet(viewsets.ModelViewSet):
-    queryset = Extra_conf.objects.all()
-    serializer_class = ExtraconfSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class PrivilegeViewSet(viewsets.ModelViewSet):
-    queryset = Privilege.objects.all()
-    serializer_class = PrivilegeSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class SudoViewSet(viewsets.ModelViewSet):
-    queryset = Sudo.objects.all()
-    serializer_class = SudoSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-class CronTableViewSet(viewsets.ModelViewSet):
-    queryset = CronTable.objects.all()
-    serializer_class = CronTableSerializer
-    permission_classes = (AdminUserRequired,)
-
-class TaskViewSet(viewsets.ModelViewSet):
-    queryset = Task.objects.all()
-    serializer_class = TaskSerializer
-    permission_classes = (AdminUserRequired,)
-
-class SubTaskViewSet(viewsets.ModelViewSet):
-    queryset = SubTask.objects.all()
-    serializer_class = SubTaskSerializer
-    permission_classes = (AdminUserRequired,)
-
-
-
diff --git a/apps/ops/models/__init__.py b/apps/ops/models/__init__.py
index b7bfa1e0d..dbd842bab 100644
--- a/apps/ops/models/__init__.py
+++ b/apps/ops/models/__init__.py
@@ -1,6 +1,4 @@
 from ansible import *
-from cron import *
-from sudo import *
 from utils import *
 from task import *
 
diff --git a/apps/ops/models/cron.py b/apps/ops/models/cron.py
deleted file mode 100644
index 766c46ece..000000000
--- a/apps/ops/models/cron.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# ~*~ coding: utf-8 ~*~
-from __future__ import unicode_literals, absolute_import
-
-import logging
-
-from django.db import models
-from assets.models import Asset
-from django.utils.translation import ugettext_lazy as _
-
-logger = logging.getLogger(__name__)
-
-__all__ = ["CronTable"]
-
-
-class CronTable(models.Model):
-    name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Name'),
-                            help_text=_("Description of a crontab entry"))
-    month = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Month'),
-                             help_text=_("Month of the year the job should run ( 1-12, *, */2, etc )"))
-    weekday = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('WeekDay'),
-                               help_text=_("Day of the week that the job should run"
-                                           " ( 0-6 for Sunday-Saturday, *, etc )"))
-    day = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Day'),
-                           help_text=_("Day of the month the job should run ( 1-31, *, */2, etc )"))
-    hour = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hour'),
-                            help_text=_("Hour when the job should run ( 0-23, *, */2, etc )"))
-    minute = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Minute'),
-                              help_text=_("Minute when the job should run ( 0-59, *, */2, etc )"))
-    job = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('Job'),
-                           help_text=_("The command to execute or, if env is set, the value of "
-                                       "environment variable. Required if state=present."))
-    user = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('User'),
-                            help_text=_("The specific user whose crontab should be modified."))
-    asset = models.ForeignKey(Asset, null=True, blank=True, related_name='crontables')
-
-    @property
-    def describe(self):
-        return "http://docs.ansible.com/ansible/cron_module.html"
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed, choice
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            cron = cls(name=forgery_py.name.full_name(),
-                       month=str(choice(range(1,13))),
-                       weekday=str(choice(range(0,7))),
-                       day=str(choice(range(1,32))),
-                       hour=str(choice(range(0,24))),
-                       minute=str(choice(range(0,60))),
-                       job=forgery_py.lorem_ipsum.sentence(),
-                       user=forgery_py.name.first_name(),
-                        )
-            try:
-                cron.save()
-                logger.debug('Generate fake cron: %s' % cron.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
\ No newline at end of file
diff --git a/apps/ops/models/sudo.py b/apps/ops/models/sudo.py
deleted file mode 100644
index 13713064c..000000000
--- a/apps/ops/models/sudo.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# ~*~ coding: utf-8 ~*~
-from __future__ import unicode_literals, absolute_import
-
-import logging
-
-from jinja2 import Template
-from django.db import models
-from django.utils.timezone import now
-from assets.models import Asset, AssetGroup
-from django.utils.translation import ugettext_lazy as _
-
-logger = logging.getLogger(__name__)
-
-__all__ = ["HostAlia", "UserAlia", "CmdAlia", "RunasAlia", "Privilege", "Extra_conf", "Sudo"]
-
-
-class HostAlia(models.Model):
-    name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Host_Alias'))
-    host_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
-
-    def __unicode__(self):
-        return self.name
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            hostA = cls(name=forgery_py.name.full_name(),
-                       host_items=forgery_py.lorem_ipsum.sentence(),
-                        )
-            try:
-                hostA.save()
-                logger.debug('Generate fake host alia: %s' % hostA.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
-
-
-class UserAlia(models.Model):
-    name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('User_Alias'))
-    user_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
-
-    def __unicode__(self):
-        return self.name
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            userA = cls(name=forgery_py.name.full_name(),
-                        user_items=forgery_py.lorem_ipsum.sentence(),
-                       )
-            try:
-                userA.save()
-                logger.debug('Generate fake host alia: %s' % userA.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
-
-
-class CmdAlia(models.Model):
-    name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Command_Alias'))
-    cmd_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
-
-    def __unicode__(self):
-        return self.name
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            cmdA = cls(name=forgery_py.name.full_name(),
-                       cmd_items=forgery_py.lorem_ipsum.sentence(),
-                       )
-            try:
-                cmdA.save()
-                logger.debug('Generate fake command alia: %s' % cmdA.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
-
-
-class RunasAlia(models.Model):
-    name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Runas_Alias'))
-    runas_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
-
-    def __unicode__(self):
-        return self.name
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            runas = cls(name=forgery_py.name.full_name(),
-                       runas_items=forgery_py.lorem_ipsum.sentence(),
-                       )
-            try:
-                runas.save()
-                logger.debug('Generate fake RunAs alia: %s' % runas.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
-
-
-class Privilege(models.Model):
-    name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
-    user = models.ForeignKey(UserAlia, blank=True, null=True, related_name='privileges')
-    host = models.ForeignKey(HostAlia, blank=True, null=True, related_name='privileges')
-    runas = models.ForeignKey(RunasAlia, blank=True, null=True, related_name='privileges')
-    command = models.ForeignKey(CmdAlia, blank=True, null=True, related_name='privileges')
-    nopassword = models.BooleanField(default=True, verbose_name=_('Is_NoPassword'))
-    comment = models.TextField(blank=True, null=True, verbose_name=_('Comment'))
-
-    def __unicode__(self):
-        return "[%s %s %s %s %s]" % (self.user.name,
-                                     self.host.name,
-                                     self.runas.name,
-                                     self.command.name,
-                                     self.nopassword)
-
-    def to_tuple(self):
-        return self.user.name, self.host.name, self.runas.name, self.command.name, self.nopassword
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed, choice
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            pri = cls(name=forgery_py.name.full_name(),
-                       comment=forgery_py.lorem_ipsum.sentence(),
-                      )
-            try:
-                pri.user = choice(UserAlia.objects.all())
-                pri.host = choice(HostAlia.objects.all())
-                pri.runas = choice(RunasAlia.objects.all())
-                pri.command = choice(CmdAlia.objects.all())
-                pri.save()
-                logger.debug('Generate fake privileges: %s' % pri.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
-
-
-class Extra_conf(models.Model):
-    line = models.TextField(blank=True, null=True, verbose_name=_('Extra_Item'),
-                            help_text=_('The extra sudo config line.'))
-
-    def __unicode__(self):
-        return self.line
-
-
-class Sudo(models.Model):
-    """
-    Sudo配置文件对象, 用于配置sudo的配置文件
-
-    :param extra_lines: <list> [<line1>, <line2>,...]
-    :param privileges: <list> [(user, host, runas, command, nopassword),]
-    """
-
-    name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'),
-                            help_text=_('Name for this sudo'))
-    created_time = models.DateTimeField(verbose_name=_('Created Time'), auto_created=True,
-                                        help_text=_('The create time of this sudo'))
-    modify_time = models.DateTimeField(auto_now=True, verbose_name=_('Modify Time'),
-                                       help_text=_('The recent modify time of this sudo'))
-    assets = models.ManyToManyField(Asset, blank=True, related_name='sudos')
-    asset_groups = models.ManyToManyField(AssetGroup, blank=True, related_name='sudos')
-    extra_lines = models.ManyToManyField(Extra_conf, related_name='sudos', blank=True)
-    privilege_items = models.ManyToManyField(Privilege, related_name='sudos', blank=True)
-
-    @property
-    def all_assets(self):
-        assets = list(self.assets.all())
-        for group in self.asset_groups.all():
-            for asset in group.assets.all():
-                if asset not in assets:
-                    assets.append(asset)
-        return assets
-
-    @property
-    def users(self):
-        return {privilege.user.name: privilege.user.user_items.split(',') for privilege in self.privilege_items.all()}
-
-    @property
-    def commands(self):
-        return {privilege.command.name: privilege.command.cmd_items.split(',') for privilege in self.privilege_items.all()}
-
-    @property
-    def hosts(self):
-        return {privilege.host.name: privilege.host.host_items.split(',') for privilege in self.privilege_items.all()}
-
-    @property
-    def runas(self):
-        return {privilege.runas.name: privilege.runas.runas_items.split(',') for privilege in self.privilege_items.all()}
-
-    @property
-    def extras(self):
-        return [extra.line for extra in self.extra_lines.all()]
-
-    @property
-    def privileges(self):
-        return [privilege.to_tuple() for privilege in self.privilege_items.all()]
-
-    @property
-    def content(self):
-        template = Template(self.__sudoers_jinja2_tmp__)
-        context = {"User_Alias": self.users,
-                   "Cmnd_Alias": self.commands,
-                   "Host_Alias": self.hosts,
-                   "Runas_Alias": self.runas,
-                   "Extra_Lines": self.extras,
-                   "Privileges": self.privileges}
-        return template.render(context)
-
-    @property
-    def __sudoers_jinja2_tmp__(self):
-        return """# management by JumpServer
-# This file MUST be edited with the 'visudo' command as root.
-#
-# Please consider adding local content in /etc/sudoers.d/ instead of
-# directly modifying this file.
-#
-# See the man page for details on how to write a sudoers file.
-#
-Defaults        env_reset
-Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-
-# JumpServer Generate Other Configure is here
-{% if Extra_Lines -%}
-{% for line in Extra_Lines -%}
-{{ line }}
-{% endfor %}
-{%- endif %}
-
-# Host alias specification
-{% if Host_Alias -%}
-{% for flag, items in Host_Alias.iteritems() -%}
-Host_Alias    {{ flag }} = {{ items|join(', ') }}
-{% endfor %}
-{%- endif %}
-
-# User alias specification
-{% if User_Alias -%}
-{% for flag, items in User_Alias.iteritems() -%}
-User_Alias    {{ flag }} = {{ items|join(', ') }}
-{% endfor %}
-{%- endif %}
-
-
-# Cmnd alias specification
-{% if Cmnd_Alias -%}
-{% for flag, items in Cmnd_Alias.iteritems() -%}
-Cmnd_Alias    {{ flag }} = {{ items|join(', ') }}
-{% endfor %}
-{%- endif %}
-
-# Run as alias specification
-{% if Runas_Alias -%}
-{% for flag, items in Runas_Alias.iteritems() -%}
-Runas_Alias    {{ flag }} = {{ items|join(', ') }}
-{% endfor %}
-{%- endif %}
-
-# User privilege specification
-root    ALL=(ALL:ALL) ALL
-
-# JumpServer Generate User privilege is here.
-# Note privileges is a tuple list like [(user, host, runas, command, nopassword),]
-{% if Privileges -%}
-{% for User_Flag, Host_Flag, Runas_Flag, Command_Flag, NopassWord in Privileges -%}
-{% if NopassWord -%}
-{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) NOPASSWD: {{ Command_Flag }}
-{%- else -%}
-{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) {{ Command_Flag }}
-{%- endif %}
-{% endfor %}
-{%- endif %}
-
-# Members of the admin group may gain root privileges
-%admin ALL=(ALL) ALL
-
-# Allow members of group sudo to execute any command
-%sudo   ALL=(ALL:ALL) ALL
-
-# See sudoers(5) for more information on "#include" directives:
-
-#includedir /etc/sudoers.d
-"""
-
-    @classmethod
-    def generate_fake(cls, count=20):
-        from random import seed, choice
-        import forgery_py
-
-        seed()
-        for i in range(count):
-            sudo = cls(name=forgery_py.name.full_name(),
-                       created_time=now()
-                      )
-            try:
-                sudo.save()
-                sudo.privilege_items = [choice(Privilege.objects.all())]
-                sudo.save()
-                logger.debug('Generate fake cron: %s' % sudo.name)
-            except Exception as e:
-                print('Error: %s, continue...' % e.message)
-                continue
\ No newline at end of file
diff --git a/apps/ops/models/utils.py b/apps/ops/models/utils.py
index 17c7cbab1..7d9f13e3d 100644
--- a/apps/ops/models/utils.py
+++ b/apps/ops/models/utils.py
@@ -2,13 +2,10 @@
 from __future__ import unicode_literals
 
 from ansible import *
-from cron import *
-from sudo import *
 
 __all__ = ["generate_fake"]
 
 
 def generate_fake():
-    for cls in (TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult, CronTable,
-                HostAlia, UserAlia, CmdAlia, RunasAlia, Privilege, Sudo):
+    for cls in (TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult):
         cls.generate_fake()
\ No newline at end of file
diff --git a/apps/ops/urls/api_urls.py b/apps/ops/urls/api_urls.py
index 77141ed35..d97d28a4a 100644
--- a/apps/ops/urls/api_urls.py
+++ b/apps/ops/urls/api_urls.py
@@ -2,20 +2,6 @@
 from __future__ import unicode_literals
 
 from rest_framework.routers import DefaultRouter
-from ops import api as v1_api
 
-__all__ = ["urlpatterns"]
 
-api_router = DefaultRouter()
-api_router.register(r'v1/host_alia', v1_api.HostAliaViewSet)
-api_router.register(r'v1/user_alia', v1_api.UserAliaViewSet)
-api_router.register(r'v1/cmd_alia', v1_api.CmdAliaViewSet)
-api_router.register(r'v1/runas_alia', v1_api.RunasAliaViewSet)
-api_router.register(r'v1/extra_conf', v1_api.ExtraconfViewSet)
-api_router.register(r'v1/privilege', v1_api.PrivilegeViewSet)
-api_router.register(r'v1/sudo', v1_api.SudoViewSet)
-api_router.register(r'v1/cron', v1_api.CronTableViewSet)
-api_router.register(r'v1/task', v1_api.TaskViewSet)
-api_router.register(r'v1/subtask', v1_api.SubTaskViewSet)
-
-urlpatterns = api_router.urls
\ No newline at end of file
+urlpatterns = []
\ No newline at end of file
diff --git a/apps/ops/urls/view_urls.py b/apps/ops/urls/view_urls.py
index 9e9c68253..4b19c3f1c 100644
--- a/apps/ops/urls/view_urls.py
+++ b/apps/ops/urls/view_urls.py
@@ -8,18 +8,6 @@ from ops import views as page_view
 __all__ = ["urlpatterns"]
 
 urlpatterns = [
-    # Resource Sudo url
-    url(r'^sudo/list$',   page_view.SudoListView.as_view(),   name='page-sudo-list'),
-    url(r'^sudo/create$', page_view.SudoCreateView.as_view(), name='page-sudo-create'),
-    url(r'^sudo/(?P<pk>[0-9]+)/detail$', page_view.SudoDetailView.as_view(), name='page-sudo-detail'),
-    url(r'^sudo/(?P<pk>[0-9]+)/update$', page_view.SudoUpdateView.as_view(), name='page-sudo-update'),
-
-    # Resource Cron url
-    url(r'^cron/list$',   page_view.CronListView.as_view(),   name='page-cron-list'),
-    url(r'^cron/create$', page_view.CronCreateView.as_view(), name='page-cron-create'),
-    url(r'^cron/(?P<pk>[0-9]+)/detail$', page_view.CronDetailView.as_view(), name='page-cron-detail'),
-    url(r'^cron/(?P<pk>[0-9]+)/update$', page_view.CronUpdateView.as_view(), name='page-cron-update'),
-
     # TResource Task url
     url(r'^task/list$',   page_view.TaskListView.as_view(),   name='page-task-list'),
     url(r'^task/create$', page_view.TaskCreateView.as_view(), name='page-task-create'),
diff --git a/apps/ops/utils/ansible_api.py b/apps/ops/utils/ansible_api.py
index 09af3a37c..38ab320b4 100644
--- a/apps/ops/utils/ansible_api.py
+++ b/apps/ops/utils/ansible_api.py
@@ -1,11 +1,12 @@
 # ~*~ coding: utf-8 ~*~
-from __future__ import unicode_literals, print_function
+# from __future__ import unicode_literals, print_function
 
 import os
 import json
 import logging
 import traceback
 import ansible.constants as default_config
+from collections import namedtuple
 
 from uuid import uuid4
 from django.utils import timezone
@@ -17,11 +18,15 @@ from ansible.executor import playbook_executor
 from ansible.utils.display import Display
 from ansible.playbook.play import Play
 from ansible.plugins.callback import CallbackBase
+import ansible.constants as C
+from ansible.utils.vars import load_extra_vars
+from ansible.utils.vars import load_options_vars
 
-from ops.models import TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult
+from ..models import TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult
 
 __all__ = ["ADHocRunner", "Options"]
 
+C.HOST_KEY_CHECKING = False
 
 logger = logging.getLogger(__name__)
 
@@ -30,149 +35,187 @@ class AnsibleError(StandardError):
     pass
 
 
-class Options(object):
-    """Ansible运行时配置类, 用于初始化Ansible的一些默认配置.
-    """
-    def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
-                 forks=10, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
-                 output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
-                 sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
-                 ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=10, ssh_common_args=None,
-                 sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
-                 syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
-        self.verbosity = verbosity
-        self.inventory = inventory
-        self.listhosts = listhosts
-        self.subset = subset
-        self.module_paths = module_paths
-        self.extra_vars = extra_vars
-        self.forks = forks
-        self.ask_vault_pass = ask_vault_pass
-        self.vault_password_files = vault_password_files
-        self.new_vault_password_file = new_vault_password_file
-        self.output_file = output_file
-        self.tags = tags
-        self.skip_tags = skip_tags
-        self.one_line = one_line
-        self.tree = tree
-        self.ask_sudo_pass = ask_sudo_pass
-        self.ask_su_pass = ask_su_pass
-        self.sudo = sudo
-        self.sudo_user = sudo_user
-        self.become = become
-        self.become_method = become_method
-        self.become_user = become_user
-        self.become_ask_pass = become_ask_pass
-        self.ask_pass = ask_pass
-        self.private_key_file = private_key_file
-        self.remote_user = remote_user
-        self.connection = connection
-        self.timeout = timeout
-        self.ssh_common_args = ssh_common_args
-        self.sftp_extra_args = sftp_extra_args
-        self.scp_extra_args = scp_extra_args
-        self.ssh_extra_args = ssh_extra_args
-        self.poll_interval = poll_interval
-        self.seconds = seconds
-        self.check = check
-        self.syntax = syntax
-        self.diff = diff
-        self.force_handlers = force_handlers
-        self.flush_cache = flush_cache
-        self.listtasks = listtasks
-        self.listtags = listtags
-        self.module_path = module_path
-        self.__overwrite_default()
-
-    def __overwrite_default(self):
-        """上面并不能包含Ansible所有的配置, 如果有其他的配置,
-        可以通过替换default_config模块里面的变量进行重载, 
-        比如 default_config.DEFAULT_ASK_PASS = False.
-        """
-        default_config.HOST_KEY_CHECKING = False
+# class Options(object):
+#     """Ansible运行时配置类, 用于初始化Ansible的一些默认配置.
+#     """
+#     def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
+#                  forks=10, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
+#                  output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
+#                  sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
+#                  ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=10, ssh_common_args=None,
+#                  sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
+#                  syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
+#         self.verbosity = verbosity
+#         self.inventory = inventory
+#         self.listhosts = listhosts
+#         self.subset = subset
+#         self.module_paths = module_paths
+#         self.extra_vars = extra_vars
+#         self.forks = forks
+#         self.ask_vault_pass = ask_vault_pass
+#         self.vault_password_files = vault_password_files
+#         self.new_vault_password_file = new_vault_password_file
+#         self.output_file = output_file
+#         self.tags = tags
+#         self.skip_tags = skip_tags
+#         self.one_line = one_line
+#         self.tree = tree
+#         self.ask_sudo_pass = ask_sudo_pass
+#         self.ask_su_pass = ask_su_pass
+#         self.sudo = sudo
+#         self.sudo_user = sudo_user
+#         self.become = become
+#         self.become_method = become_method
+#         self.become_user = become_user
+#         self.become_ask_pass = become_ask_pass
+#         self.ask_pass = ask_pass
+#         self.private_key_file = private_key_file
+#         self.remote_user = remote_user
+#         self.connection = connection
+#         self.timeout = timeout
+#         self.ssh_common_args = ssh_common_args
+#         self.sftp_extra_args = sftp_extra_args
+#         self.scp_extra_args = scp_extra_args
+#         self.ssh_extra_args = ssh_extra_args
+#         self.poll_interval = poll_interval
+#         self.seconds = seconds
+#         self.check = check
+#         self.syntax = syntax
+#         self.diff = diff
+#         self.force_handlers = force_handlers
+#         self.flush_cache = flush_cache
+#         self.listtasks = listtasks
+#         self.listtags = listtags
+#         self.module_path = module_path
+#         self.__overwrite_default()
+#
+#     def __overwrite_default(self):
+#         """上面并不能包含Ansible所有的配置, 如果有其他的配置,
+#         可以通过替换default_config模块里面的变量进行重载, 
+#         比如 default_config.DEFAULT_ASK_PASS = False.
+#         """
+#         default_config.HOST_KEY_CHECKING = False
+Options = namedtuple("Options", [
+    'connection', 'module_path', 'private_key_file', "remote_user", "timeout",
+    'forks', 'become', 'become_method', 'become_user', 'check', "extra_vars",
+    ]
+)
 
 
-class InventoryMixin(object):
+class JMSHost(Host):
+    def __init__(self, asset):
+        self.asset = asset
+        self.name = name = asset.get('hostname') or asset.get('ip')
+        self.port = port = asset.get('port') or 22
+        super(JMSHost, self).__init__(name, port)
+        self.set_all_variable()
+
+    def set_all_variable(self):
+        asset = self.asset
+        self.set_variable('ansible_host', asset['ip'])
+        self.set_variable('ansible_port', asset['port'])
+        self.set_variable('ansible_user', asset['username'])
+
+        # 添加密码和秘钥
+        if asset.get('password'):
+            self.set_variable('ansible_ssh_pass', asset['password'])
+        if asset.get('key'):
+            self.set_variable('ansible_ssh_private_key_file', asset['private_key'])
+
+        # 添加become支持
+        become = asset.get("become", None)
+        if become is not None:
+            self.set_variable("ansible_become", True)
+            self.set_variable("ansible_become_method", become.get('method'))
+            self.set_variable("ansible_become_user", become.get('user'))
+            self.set_variable("ansible_become_pass", become.get('pass'))
+        else:
+            self.set_variable("ansible_become", False)
+
+
+class JMSInventory(Inventory):
     """
     提供生成Ansible inventory对象的方法
     """
 
-    def gen_inventory(self):
+    def __init__(self, host_list=None):
+        if host_list is None:
+            host_list = []
+        assert isinstance(host_list, list)
+        self.host_list = host_list
+        self.loader = DataLoader()
+        self.variable_manager = VariableManager()
+        super(JMSInventory, self).__init__(self.loader, self.variable_manager,
+                                           host_list=host_list)
+
+    def parse_inventory(self, host_list):
         """用于生成动态构建Ansible Inventory.
-        self.hosts: [
-                        {"host": <ip>,
-                          "port": <port>,
-                          "user": <user>,
-                          "pass": <pass>,
-                          "key": <sshKey>,
-                          "group": <default>
-                          "other_host_var": <other>},
-                        {...},
-                     ]
-        self.group_vars: {
-            "groupName1": {"var1": <value>, "var2": <value>, ...},
-            "groupName2": {"var1": <value>, "var2": <value>, ...},
-        }
+        self.host_list: [
+            {"name": "asset_name",
+             "ip": <ip>,
+             "port": <port>,
+             "user": <user>,
+             "pass": <pass>,
+             "key": <sshKey>,
+             "groups": ['group1', 'group2'],
+             "other_host_var": <other>},
+             {...},
+        ]
 
         :return: 返回一个Ansible的inventory对象
         """
 
         # TODO: 验证输入
-
         # 创建Ansible Group,如果没有则创建default组
-        for asset in self.hosts:
-            g_name = asset.get('group', 'default')
-            if g_name not in [g.name for g in self.groups]:
-                group = Group(name=g_name)
-                self.groups.append(group)
+        ungrouped = Group('ungrouped')
+        all = Group('all')
+        all.add_child_group(ungrouped)
+        self.groups = dict(all=all, ungrouped=ungrouped)
 
-        # 添加组变量到相应的组上
-        for group_name, variables in self.group_vars.iteritems():
-            for g in self.groups:
-                if g.name == group_name:
-                    for v_name, v_value in variables.iteritems():
-                        g.set_variable(v_name, v_value)
-
-        # 往组里面添加Host
-        for asset in self.hosts:
-            # 添加Host链接的常用变量(host,port,user,pass,key)
-            host = Host(name=asset['name'], port=asset['port'])
-            host.set_variable('ansible_host', asset['ip'])
-            host.set_variable('ansible_port', asset['port'])
-            host.set_variable('ansible_user', asset['username'])
-
-            # 添加密码和秘钥
-            if asset.get('password'):
-                host.set_variable('ansible_ssh_pass', asset['password'])
-            if asset.get('key'):
-                host.set_variable('ansible_ssh_private_key_file', asset['key'])
-
-            # 添加become支持
-            become = asset.get("become", None)
-            if become is not None:
-                host.set_variable("ansible_become", True)
-                host.set_variable("ansible_become_method", become.get('method'))
-                host.set_variable("ansible_become_user", become.get('user'))
-                host.set_variable("ansible_become_pass", become.get('pass'))
+        for asset in host_list:
+            host = JMSHost(asset=asset)
+            asset_groups = asset.get('groups')
+            if asset_groups:
+                for group_name in asset_groups:
+                    if group_name not in self.groups:
+                        group = Group(group_name)
+                        self.groups[group_name] = group
+                    else:
+                        group = self.groups[group_name]
+                    group.add_host(host)
             else:
-                host.set_variable("ansible_become", False)
+                ungrouped.add_host(host)
+            all.add_host(host)
 
-            # 添加其他Host的额外变量
-            for key, value in asset.iteritems():
-                if key not in ["name", "port", "ip", "username", "password", "key"]:
-                    host.set_variable(key, value)
 
-            # 将host添加到组里面
-            for g in self.groups:
-                if g.name == asset.get('group', 'default'):
-                    g.add_host(host)
+class BasicResultCallback(CallbackBase):
+    """
+    Custom Callback
+    """
+    def __init__(self, display=None):
+        self.result_q = dict(contacted={}, dark={})
+        super(BasicResultCallback, self).__init__(display)
 
-        # 将组添加到Inventory里面,生成真正的inventory对象
-        inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
-        for g in self.groups:
-            inventory.add_group(g)
-        self.variable_manager.set_inventory(inventory)
-        return inventory
+    def gather_result(self, n, res):
+        self.result_q[n].update({res._host.name: res._result})
+
+    def v2_runner_on_ok(self, result):
+        self.gather_result("contacted", result)
+
+    def v2_runner_on_failed(self, result, ignore_errors=False):
+        self.gather_result("dark", result)
+
+    def v2_runner_on_unreachable(self, result):
+        self.gather_result("dark", result)
+
+    def v2_runner_on_skipped(self, result):
+        self.gather_result("dark", result)
+
+    def v2_playbook_on_task_start(self, task, is_conditional):
+        pass
+
+    def v2_playbook_on_play_start(self, play):
+        pass
 
 
 class CallbackModule(CallbackBase):
@@ -310,7 +353,7 @@ class CallbackModule(CallbackBase):
         print("summary: %s", summary)
 
 
-class PlayBookRunner(InventoryMixin):
+class PlayBookRunner(object):
     """用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
     """
 
@@ -393,133 +436,121 @@ class PlayBookRunner(InventoryMixin):
         return stats
 
 
-class ADHocRunner(InventoryMixin):
+class ADHocRunner(object):
     """
     ADHoc接口
     """
-    def __init__(self, play_data, config=None, *hosts, **group_vars):
-        """
-        :param hosts: 见PlaybookRunner参数
-        :param group_vars: 见PlaybookRunner参数
-        :param config: Config实例
+    def __init__(self,
+                 hosts=C.DEFAULT_HOST_LIST,
+                 module_name=C.DEFAULT_MODULE_NAME,  # * command
+                 module_args=C.DEFAULT_MODULE_ARGS,  # * 'cmd args'
+                 forks=C.DEFAULT_FORKS,  # 5
+                 timeout=C.DEFAULT_TIMEOUT,  # SSH timeout = 10s
+                 pattern="all",  # all
+                 remote_user=C.DEFAULT_REMOTE_USER,  # root
+                 module_path=None,  # dirs of custome modules
+                 connection_type="smart",
+                 become=None,
+                 become_method=None,
+                 become_user=None,
+                 check=False,
+                 passwords=None,
+                 extra_vars=None,
+                 private_key_file=None,
+                 gather_facts='no'):
 
-        :param play_data:
-        play_data = dict(
-            name="Ansible Ad-Hoc",
-            hosts=pattern,
-            gather_facts=True,
-            tasks=[dict(action=dict(module='service', args={'name': 'vsftpd', 'state': 'restarted'}), async=async, poll=poll)]
-        )
-        """
-        self.options = config if config != None else Options()
-
-        # 设置verbosity级别, 及命令行的--verbose选项
-        self.display = Display()
-        self.display.verbosity = self.options.verbosity
-
-        # sudo的配置移到了Host级别去了,因此这里不再需要处理
-        self.passwords = None
-
-        # 生成Ansible inventory, 这些变量Mixin都会用到
-        self.hosts = hosts
-        self.group_vars = group_vars
-        self.loader = DataLoader()
+        self.pattern = pattern
         self.variable_manager = VariableManager()
-        self.groups = []
-        self.inventory = self.gen_inventory()
+        self.loader = DataLoader()
+        self.module_name = module_name
+        self.module_args = module_args
+        self.check_module_args()
+        self.gather_facts = gather_facts
+        self.results_callback = BasicResultCallback()
+        self.options = Options(
+            connection=connection_type,
+            timeout=timeout,
+            module_path=module_path,
+            forks=forks,
+            become=become,
+            become_method=become_method,
+            become_user=become_user,
+            check=check,
+            remote_user=remote_user,
+            extra_vars=extra_vars or [],
+            private_key_file=private_key_file,
+        )
 
-        self.play = Play().load(play_data, variable_manager=self.variable_manager, loader=self.loader)
+        self.variable_manager.extra_vars = load_extra_vars(self.loader, options=self.options)
+        self.variable_manager.options_vars = load_options_vars(self.options)
+        self.passwords = passwords or {}
+        self.inventory = JMSInventory(hosts)
+        self.variable_manager.set_inventory(self.inventory)
+
+        self.play_source = dict(
+            name='Ansible Ad-hoc',
+            hosts=self.pattern,
+            gather_facts=self.gather_facts,
+            tasks=[dict(action=dict(
+                module=self.module_name,
+                args=self.module_args
+            ))]
+        )
+
+        self.play = Play().load(
+            self.play_source,
+            variable_manager=self.variable_manager,
+            loader=self.loader,
+        )
+
+        self.runner = TaskQueueManager(
+            inventory=self.inventory,
+            variable_manager=self.variable_manager,
+            loader=self.loader,
+            options=self.options,
+            passwords=self.passwords,
+            stdout_callback=self.results_callback,
+        )
+
+    def check_module_args(self):
+        if self.module_name in C.MODULE_REQUIRE_ARGS and not self.module_args:
+            err = "No argument passed to '%s' module." % self.module_name
+            raise AnsibleError(err)
+
+    def run(self):
+        if not self.inventory.list_hosts("all"):
+            raise AnsibleError("Inventory is empty.")
+
+        if not self.inventory.list_hosts(self.pattern):
+            raise AnsibleError(
+                "pattern: %s  dose not match any hosts." % self.pattern)
 
-    @staticmethod
-    def update_db_tasker(tasker_id, ext_code):
         try:
-            tasker = TaskRecord.objects.get(uuid=tasker_id)
-            tasker.end = timezone.now()
-            tasker.completed = True
-            tasker.exit_code = ext_code
-            tasker.save()
+            self.runner.run(self.play)
         except Exception as e:
-            logger.error("Update Tasker Status into database error!, %s" % e.message)
-
-    def create_db_tasker(self, name, uuid):
-        try:
-            hosts = [host.get('name') for host in self.hosts]
-            tasker = TaskRecord(name=name, uuid=uuid, hosts=','.join(hosts), start=timezone.now())
-            tasker.save()
-        except Exception as e:
-            logger.error("Save Tasker to database error!, %s" % e.message)
-
-    def run(self, tasker_name, tasker_uuid):
-        """执行ADHoc, 执行完后, 修改AnsiblePlay的状态为完成状态.
-
-        :param tasker_uuid <str> 用于标示此次task
-        """
-        # 初始化callback插件,以及Tasker
-
-        self.create_db_tasker(tasker_name, tasker_uuid)
-        self.results_callback = CallbackModule(tasker_uuid)
-
-        tqm = None
-        # TODO:日志和结果分析
-        try:
-            tqm = TaskQueueManager(
-                inventory=self.inventory,
-                variable_manager=self.variable_manager,
-                loader=self.loader,
-                stdout_callback=self.results_callback,
-                options=self.options,
-                passwords=self.passwords
-            )
-            ext_code = tqm.run(self.play)
-            result = self.results_callback.results
-
-            # 任务运行结束, 标示任务完成
-            self.update_db_tasker(tasker_uuid, ext_code)
-
-            ret = json.dumps(result)
-            return ext_code, ret
-
+            pass
+        else:
+            return self.results_callback.result_q
         finally:
-            if tqm:
-                tqm.cleanup()
+            if self.runner:
+                self.runner.cleanup()
+            if self.loader:
+                self.loader.cleanup_all_tmp_files()
 
 
 def test_run():
-    conf = Options()
     assets = [
         {
-                "name": "192.168.1.119",
-                "ip": "192.168.1.119",
-                "port": "22",
+                "hostname": "192.168.152.129",
+                "ip": "192.168.152.129",
+                "port": 22,
                 "username": "root",
-                "password": "tongfang_test",
-                "key": "asset_private_key",
+                "password": "redhat",
         },
-        {
-                "name": "192.168.232.135",
-                "ip": "192.168.232.135",
-                "port": "22",
-                "username": "yumaojun",
-                "password": "xxx",
-                "key": "asset_private_key",
-                "become": {"method": "sudo", "user": "root", "pass": "xxx"}
-    },
     ]
-    # 初始化Play
-    play_source = {
-            "name": "Ansible Play",
-            "hosts": "default",
-            "gather_facts": "no",
-            "tasks": [
-                dict(action=dict(module='ping')),
-            ]
-        }
-    hoc = ADHocRunner(conf, play_source, *assets)
-    uuid = "tasker-" + uuid4().hex
-    ext_code, result = hoc.run("test_task", uuid)
-    print(ext_code)
-    print(result)
-
+    hoc = ADHocRunner(module_name='shell', module_args='ls', hosts=assets)
+    ret = hoc.run()
+    print(ret)
 
 if __name__ == "__main__":
     test_run()
diff --git a/apps/ops/views.py b/apps/ops/views.py
index cf07aee59..c897cde79 100644
--- a/apps/ops/views.py
+++ b/apps/ops/views.py
@@ -8,54 +8,9 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
 
 from users.utils import AdminUserRequiredMixin
 from ops.utils.mixins import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
-from ops.models import  *
+from .models import Task
 
 
-class SudoListView(AdminUserRequiredMixin, ListSudoPrivilegesMixin, ListView):
-    paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
-    model = Sudo
-    context_object_name = 'sudos'
-    template_name = 'sudo/list.html'
-
-
-class SudoCreateView(AdminUserRequiredMixin, CreateSudoPrivilegesMixin, CreateView):
-    model = Sudo
-    template_name = 'sudo/create.html'
-
-
-class SudoUpdateView(AdminUserRequiredMixin, UpdateView):
-    model = Sudo
-    template_name = 'sudo/update.html'
-
-
-class SudoDetailView(DetailView):
-    model = Sudo
-    context_object_name = 'sudo'
-    template_name = 'sudo/detail.html'
-
-
-class CronListView(AdminUserRequiredMixin, ListView):
-    paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
-    model = CronTable
-    context_object_name = 'crons'
-    template_name = 'cron/list.html'
-
-
-class CronCreateView(AdminUserRequiredMixin, CreateView):
-    model = CronTable
-    template_name = 'cron/create.html'
-
-
-class CronUpdateView(AdminUserRequiredMixin, UpdateView):
-    model = CronTable
-    template_name = 'cron/update.html'
-
-
-class CronDetailView(DetailView):
-    model = CronTable
-    context_object_name = 'cron'
-    template_name = 'cron/detail.html'
-
 class TaskListView(AdminUserRequiredMixin, ListView):
     paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
     model = Task