# Path: ssheepdog/utils.py
# class DirtyFieldsMixin(object):
#     """
#     Supplies method get_dirty_fields() which is a dict of those fields which
#     have changed since object creation, mapping field names to original values.
# 
#     A foreign key field is dirty if its pk changes; changes to the other object
#     are not detected.  The dirty field is listed as fieldname_pk, and the
#     original value is either the pk itself or None.
# 
#     Many-to-many fields are ignored.
#     """
#     def __init__(self, *args, **kwargs):
#         super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
#         self._original_state = self._as_dict()
# 
#     def _as_dict(self):
#         def name(f):
#             return "%s_pk" % f.name if f.rel else f.name
# 
#         def value(f):
#             if f.rel: # Usually the primary key is the "_id"...
#                 try:
#                     return getattr(self, f.name + "_id")
#                 except AttributeError:
#                     pass
# 
#             try:
#                 val = getattr(self, f.name)
#             except ObjectDoesNotExist: # foreign key relation not yet set
#                 val = None
#             if f.rel and val:
#                 return val.pk
#             else:
#                 return val
#         return dict([(name(f), value(f)) for f in self._meta.local_fields])
# 
#     def get_dirty_fields(self):
#         new_state = self._as_dict()
#         return dict([(key, value)
#                      for key, value in self._original_state.iteritems()
#                      if value != new_state[key]])
# 
# class capture_output(object):
#     """
#     Usage:
#     with capture_output() as captured:
#         do_stuff()
#     Now captured.stderr and captured.stdout contain the output generated during
#     do_stuff().  capture_output(stderr=False) turns the latter off.
#     """
# 
#     backup = None
#     result = None
# 
#     def __init__(self, stderr=True, stdout=True):
#         self.capture_stdout = stdout
#         self.capture_stderr = stderr
# 
#     def __enter__(self):
#         class Result(object):
#             stdout = ""
#             stderr = ""
#         self.result = Result()
#         if self.capture_stderr:
#             self.stderr = sys.stderr
#             sys.stderr = StringIO()
#         if self.capture_stdout:
#             self.stdout = sys.stdout
#             sys.stdout = StringIO()
#         return self.result
# 
#     def __exit__(self, type, value, traceback):
#         if self.capture_stderr:
#             self.result.stderr = sys.stderr.getvalue()
#             sys.stderr.close()
#             sys.stderr = self.stderr
#         if self.capture_stdout:
#             self.result.stdout = sys.stdout.getvalue()
#             sys.stdout.close()
#             sys.stdout = self.stdout
# 
# Path: ssheepdog/fields.py
# class PublicKeyField(TextField):
#     def validate(self, value, model_instance):
#         """
#         Confirm that each row is a valid ssh key
#         """
#         def _validate_key(value):
#             """
#             Just confirm that the first field is something like ssh-rsa or ssh-dss,
#             and the second field is reasonably long and can be base64 decoded.
#             """
#             if value.strip() == "":
#                 return True
#             try:
#                 type_, key_string = value.split()[:2]
#                 assert (type_[:4] == 'ssh-')
#                 assert (len(key_string) > 100)
#                 base64.decodestring(key_string)
#                 return True
#             except:
#                 False
# 
#         super(PublicKeyField, self).validate(value, model_instance)
#         keys = value.rstrip().split("\n")
#         l = len(keys)
#         i = 0
#         for s in value.split("\n"):
#             i += 1
#             if not _validate_key(s):
#                 if l == 1:
#                     message = _("This does not appear to be an ssh public key")
#                 else:
#                     message = _("Row %d does not appear"
#                                 " to be an ssh public key") % i
#                 raise exceptions.ValidationError(message)
# 
#     def clean(self, value, model_instance):
#         """
#         Clean up any whitespace.
#         """
#         lines = value.strip().split("\n")
#         lines = [" ".join(line.strip().split()) for line in lines]
#         value = "\n".join([line for line in lines if line])
#         return super(PublicKeyField, self).clean(value, model_instance)


import os, base64
import fabric.exceptions
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save, m2m_changed
from django.db.utils import DatabaseError
from fabric.api import env, run, hide, settings
from fabric.network import disconnect_all
from django.conf import settings as app_settings
from ssheepdog.utils import DirtyFieldsMixin, capture_output
from django.core.urlresolvers import reverse
from south.signals import post_migrate
from south.modelsinspector import add_introspection_rules
from Crypto.PublicKey import RSA
from Crypto import Random
from ssheepdog.fields import PublicKeyField
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError

    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        # Updates to these fields require a push of keys to all logins
        fields = set(['hostname', 'ip', 'port', 'is_active'])
        made_dirty = bool(fields.intersection(dirty_fields))
        if made_dirty:
            self.login_set.update(is_dirty=True)

        super(Machine, self).save(*args, **kwargs)

        # if the client changed, make login clients change which used to match
        if 'client_pk' in dirty_fields:
            old_pk = dirty_fields['client_pk']
            self.login_set.filter(client__pk=old_pk).update(client=self.client)
            self.login_set.filter(client__pk=None).update(client=self.client)


class LoginLog(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    message = models.TextField()
    stderr = models.TextField(default="")
    stdout = models.TextField(default="")
    login = models.ForeignKey('Login', null=True)
    actor = models.ForeignKey(User, null=True)
    class Meta:
        ordering = ['-date']



# Path: ssheepdog/utils.py
# class DirtyFieldsMixin(object):
#     """
#     Supplies method get_dirty_fields() which is a dict of those fields which
#     have changed since object creation, mapping field names to original values.
# 
#     A foreign key field is dirty if its pk changes; changes to the other object
#     are not detected.  The dirty field is listed as fieldname_pk, and the
#     original value is either the pk itself or None.
# 
#     Many-to-many fields are ignored.
#     """
#     def __init__(self, *args, **kwargs):
#         super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
#         self._original_state = self._as_dict()
# 
#     def _as_dict(self):
#         def name(f):
#             return "%s_pk" % f.name if f.rel else f.name
# 
#         def value(f):
#             if f.rel: # Usually the primary key is the "_id"...
#                 try:
#                     return getattr(self, f.name + "_id")
#                 except AttributeError:
#                     pass
# 
#             try:
#                 val = getattr(self, f.name)
#             except ObjectDoesNotExist: # foreign key relation not yet set
#                 val = None
#             if f.rel and val:
#                 return val.pk
#             else:
#                 return val
#         return dict([(name(f), value(f)) for f in self._meta.local_fields])
# 
#     def get_dirty_fields(self):
#         new_state = self._as_dict()
#         return dict([(key, value)
#                      for key, value in self._original_state.iteritems()
#                      if value != new_state[key]])
# 
# class capture_output(object):
#     """
#     Usage:
#     with capture_output() as captured:
#         do_stuff()
#     Now captured.stderr and captured.stdout contain the output generated during
#     do_stuff().  capture_output(stderr=False) turns the latter off.
#     """
# 
#     backup = None
#     result = None
# 
#     def __init__(self, stderr=True, stdout=True):
#         self.capture_stdout = stdout
#         self.capture_stderr = stderr
# 
#     def __enter__(self):
#         class Result(object):
#             stdout = ""
#             stderr = ""
#         self.result = Result()
#         if self.capture_stderr:
#             self.stderr = sys.stderr
#             sys.stderr = StringIO()
#         if self.capture_stdout:
#             self.stdout = sys.stdout
#             sys.stdout = StringIO()
#         return self.result
# 
#     def __exit__(self, type, value, traceback):
#         if self.capture_stderr:
#             self.result.stderr = sys.stderr.getvalue()
#             sys.stderr.close()
#             sys.stderr = self.stderr
#         if self.capture_stdout:
#             self.result.stdout = sys.stdout.getvalue()
#             sys.stdout.close()
#             sys.stdout = self.stdout
# 
# Path: ssheepdog/fields.py
# class PublicKeyField(TextField):
#     def validate(self, value, model_instance):
#         """
#         Confirm that each row is a valid ssh key
#         """
#         def _validate_key(value):
#             """
#             Just confirm that the first field is something like ssh-rsa or ssh-dss,
#             and the second field is reasonably long and can be base64 decoded.
#             """
#             if value.strip() == "":
#                 return True
#             try:
#                 type_, key_string = value.split()[:2]
#                 assert (type_[:4] == 'ssh-')
#                 assert (len(key_string) > 100)
#                 base64.decodestring(key_string)
#                 return True
#             except:
#                 False
# 
#         super(PublicKeyField, self).validate(value, model_instance)
#         keys = value.rstrip().split("\n")
#         l = len(keys)
#         i = 0
#         for s in value.split("\n"):
#             i += 1
#             if not _validate_key(s):
#                 if l == 1:
#                     message = _("This does not appear to be an ssh public key")
#                 else:
#                     message = _("Row %d does not appear"
#                                 " to be an ssh public key") % i
#                 raise exceptions.ValidationError(message)
# 
#     def clean(self, value, model_instance):
#         """
#         Clean up any whitespace.
#         """
#         lines = value.strip().split("\n")
#         lines = [" ".join(line.strip().split()) for line in lines]
#         value = "\n".join([line for line in lines if line])
#         return super(PublicKeyField, self).clean(value, model_instance)


# Path: ssheepdog/models.py
import os, base64
import fabric.exceptions
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save, m2m_changed
from django.db.utils import DatabaseError
from fabric.api import env, run, hide, settings
from fabric.network import disconnect_all
from django.conf import settings as app_settings
from ssheepdog.utils import DirtyFieldsMixin, capture_output
from django.core.urlresolvers import reverse
from south.signals import post_migrate
from south.modelsinspector import add_introspection_rules
from Crypto.PublicKey import RSA
from Crypto import Random
from ssheepdog.fields import PublicKeyField
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError


    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        # Updates to these fields require a push of keys to all logins
        fields = set(['hostname', 'ip', 'port', 'is_active'])
        made_dirty = bool(fields.intersection(dirty_fields))
        if made_dirty:
            self.login_set.update(is_dirty=True)

        super(Machine, self).save(*args, **kwargs)

        # if the client changed, make login clients change which used to match
        if 'client_pk' in dirty_fields:
            old_pk = dirty_fields['client_pk']
            self.login_set.filter(client__pk=old_pk).update(client=self.client)
            self.login_set.filter(client__pk=None).update(client=self.client)


class LoginLog(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    message = models.TextField()
    stderr = models.TextField(default="")
    stdout = models.TextField(default="")
    login = models.ForeignKey('Login', null=True)
    actor = models.ForeignKey(User, null=True)
    class Meta:
        ordering = ['-date']



class Login(DirtyFieldsMixin, models.Model):