Beispiel #1
0
class Site(models.Model):
    name = models.CharField(max_length=40, primary_key=True)
    description = models.CharField(max_length=200)
    parent = models.ForeignKey('self',
                               null=True,
                               blank=True,
                               on_delete=models.CASCADE)
    config_values = MapField(blank=True)
    updated = models.DateTimeField(editable=False, auto_now=True)
    created = models.DateTimeField(editable=False, auto_now_add=True)

    @cinp.action('Map')
    def getConfig(self):
        return getConfig(self)

    @cinp.check_auth()
    @staticmethod
    def checkAuth(user, method, id_list, action=None):
        return True

    def clean(self, *args, **kwargs):
        super().clean(*args, **kwargs)
        errors = {}

        if not name_regex.match(self.name):
            errors['name'] = 'name "{0}" is invalid'.format(self.name)

        for name in self.config_values:
            if not config_name_regex.match(name):
                errors[
                    'config_values'] = 'config item name "{0}" is invalid'.format(
                        name)
                break

        if errors:
            raise ValidationError(errors)

    def __str__(self):
        return 'Site "{0}"({1})'.format(self.description, self.name)
Beispiel #2
0
class Box(models.Model):
    BOX_TYPE = (('post', 'POST'), ('call', 'call (CINP)'))
    url = models.CharField(max_length=2048)
    proxy = models.CharField(max_length=512, blank=True, null=True)
    type = models.CharField(max_length=4, choices=BOX_TYPE)
    one_shot = models.BooleanField(default=True)
    extra_data = MapField()
    expires = models.DateTimeField(blank=True, null=True)
    updated = models.DateTimeField(editable=False, auto_now=True)
    created = models.DateTimeField(editable=False, auto_now_add=True)

    def extend(self, additional_hours):
        self.expires += timedelta(hour=additional_hours)
        self.full_clean()
        self.save()

    def clean(self, *args, **kwargs):
        super().clean(*args, **kwargs)
        if not self.expires:
            self.expires = None

        if not self.proxy:
            self.proxy = None

        errors = {}
        if self.expires is not None and not self.expires > datetime.now(
                timezone.utc) + timedelta(hours=MAX_BOX_LIFE):
            errors['expires'] = 'more than "{0}" hourse in the future'.format(
                MAX_BOX_LIFE)

        if not self.one_shot and self.exires is None:
            errors['expires'] = 'required when not one_shot'

        if errors:
            raise ValidationError(errors)

    class Meta:
        abstract = True
Beispiel #3
0
class BluePrint(models.Model):
    name = models.CharField(
        max_length=40,
        primary_key=True)  # update Architect if this changes max_length
    description = models.CharField(max_length=200)
    scripts = models.ManyToManyField('Script', through='BluePrintScript')
    config_values = MapField(blank=True, null=True)
    updated = models.DateTimeField(editable=False, auto_now=True)
    created = models.DateTimeField(editable=False, auto_now_add=True)

    def get_script(self, name):
        try:
            return self.blueprintscript_set.get(name=name).script.script
        except BluePrintScript.DoesNotExist:
            for parent in self.parent_list.all():
                tmp = parent.get_script(name)
                if tmp is not None:
                    return tmp

            return None

    @property
    def subclass(self):
        try:
            return self.foundationblueprint
        except AttributeError:
            pass

        try:
            return self.structureblueprint
        except AttributeError:
            pass

        return self

    @cinp.action('Map')
    def getConfig(self):
        return getConfig(self.subclass)

    @cinp.check_auth()
    @staticmethod
    def checkAuth(user, verb, id_list, action=None):
        if verb in ('DESCRIBE', 'CALL'):
            return True

        return False

    def clean(self, *args, **kwargs):
        super().clean(*args, **kwargs)
        errors = {}
        if not name_regex.match(
                self.name
        ):  # if this regex changes, make sure to update tcalc parser in archetect
            errors['name'] = 'BluePrint Script name "{0}" is invalid'.format(
                self.name)

        if self.config_values is not None:
            for name in self.config_values:
                if not config_name_regex.match(name):
                    errors[
                        'config_values'] = 'config item name "{0}" is invalid'.format(
                            name)
                    break

        if errors:
            raise ValidationError(errors)

    def __str__(self):
        return 'BluePrint "{0}"({1})'.format(self.description, self.name)
Beispiel #4
0
class Site( models.Model ):
  name = models.CharField( max_length=40, primary_key=True )  # update Architect if this changes max_length
  zone = models.ForeignKey( Zone, null=True, blank=True, on_delete=models.PROTECT )
  description = models.CharField( max_length=200 )
  parent = models.ForeignKey( 'self', null=True, blank=True, on_delete=models.CASCADE )
  config_values = MapField( blank=True, null=True )
  updated = models.DateTimeField( editable=False, auto_now=True )
  created = models.DateTimeField( editable=False, auto_now_add=True )

  @cinp.action( 'Map' )
  def getConfig( self ):
    return getConfig( self )

  @cinp.action( 'Map' )
  def getDependencyMap( self ):
    from contractor.Building.models import Dependency
    result = {}
    external_list = []
    structure_job_list = [ i.structurejob.structure.pk for i in self.basejob_set.filter( structurejob__isnull=False ) ]
    foundation_job_list = [ i.foundationjob.foundation.pk for i in self.basejob_set.filter( foundationjob__isnull=False ) ]
    dependency_job_list = [ i.dependencyjob.dependency.pk for i in self.basejob_set.filter( dependencyjob__isnull=False ) ]

    for structure in self.networked_set.filter( structure__isnull=False ).order_by( 'pk' ):
      structure = structure.structure
      dependency_list = [ structure.foundation.dependencyId ]
      if structure.foundation.site != self:
        external_list.append( structure.foundation )

      result[ structure.dependencyId ] = { 'description': structure.description, 'type': 'Structure', 'state': structure.state, 'dependency_list': dependency_list, 'has_job': ( structure.pk in structure_job_list ), 'external': False }

    for foundation in self.foundation_set.all().order_by( 'pk' ):
      foundation = foundation.subclass
      # dependency_list = list( set( [ i.dependencyId for i in foundation.dependency_set.all() ] ) )
      dependency_list = []
      if foundation.dependency:
        dependency_list.append( foundation.dependency.dependencyId )

      try:
        dependency_list += [ foundation.complex.dependencyId ]
        if foundation.complex.site != self:
          external_list += [ foundation.complex  ]
      except AttributeError:
        pass

      result[ foundation.dependencyId ] = { 'description': foundation.description, 'type': 'Foundation', 'state': foundation.state, 'dependency_list': dependency_list, 'has_job': ( foundation.pk in foundation_job_list ), 'external': False }

    for dependency in Dependency.objects.filter( Q( foundation__site=self ) |
                                                 Q( foundation__isnull=True, script_structure__site=self ) |
                                                 Q( foundation__isnull=True, script_structure__isnull=True, dependency__structure__site=self ) |
                                                 Q( foundation__isnull=True, script_structure__isnull=True, structure__site=self )
                                                 ).order_by( 'pk' ):

      if dependency.dependency is not None:
        dependency_list = [ dependency.dependency.dependencyId ]
      else:
        dependency_list = [ dependency.structure.dependencyId ]
      if dependency.site != self:
        external_list += [ dependency.structure ]

      result[ dependency.dependencyId ] = { 'description': dependency.description, 'type': 'Dependency', 'state': dependency.state, 'dependency_list': dependency_list, 'has_job': ( dependency.pk in dependency_job_list ), 'external': False }

    for complex in self.complex_set.all().order_by( 'pk' ):
      complex = complex.subclass
      dependency_list = [ i.structure.dependencyId for i in complex.complexstructure_set.all() ]
      external_list += [ i.structure if i.structure.site != self else None for i in complex.complexstructure_set.all() ]

      result[ complex.dependencyId ] = { 'description': complex.description, 'type': 'Complex', 'state': complex.state, 'dependency_list': dependency_list, 'external': False }

    external_list = list( set( external_list ) )

    for external in external_list:
      if external is None:
        continue

      result[ external.dependencyId ] = { 'description': external.description, 'type': external.type, 'state': external.state, 'dependency_list': [], 'external': True }

    return result

  @cinp.check_auth()
  @staticmethod
  def checkAuth( user, verb, id_list, action=None ):
    return True

  def clean( self, *args, **kwargs ):
    super().clean( *args, **kwargs )
    errors = {}

    if self.name and not name_regex.match( self.name ):
      errors[ 'name' ] = 'Invalid'

    if self.config_values is not None:
      for name in self.config_values:
        if not config_name_regex.match( name ):
          errors[ 'config_values' ] = 'config item name "{0}" is invalid'.format( name )
          break

        if name in ( 'domain_search', 'dns_servers', 'log_servers' ):
          if not isinstance( self.config_values[ name ], list ):
            errors[ 'config_values' ] = 'config item "{0}" must be a list'.format( name )
          break

    if errors:
      raise ValidationError( errors )

  def __str__( self ):
    return 'Site "{0}"({1})'.format( self.description, self.name )
Beispiel #5
0
class Structure( Networked ):
  blueprint = models.ForeignKey( StructureBluePrint, on_delete=models.PROTECT )  # ie what to bild
  foundation = models.OneToOneField( Foundation, related_name='+', on_delete=models.PROTECT )      # ie what to build it on
  config_uuid = models.CharField( max_length=36, default=getUUID, unique=True )  # unique
  config_values = MapField( blank=True, null=True )
  built_at = models.DateTimeField( editable=False, blank=True, null=True )
  updated = models.DateTimeField( editable=False, auto_now=True )
  created = models.DateTimeField( editable=False, auto_now_add=True )

  def _canSetState( self, job=None ):
    try:
      return self.structurejob == job
    except ObjectDoesNotExist:
      pass

    return True

  def setBuilt( self, job=None ):
    if not self._canSetState( job ):
      raise Exception( 'All related jobs must be cleared before setting Built' )

    self.built_at = timezone.now()
    self.full_clean()
    self.save()

  def setDestroyed( self, job=None ):
    if not self._canSetState( job ):
      raise Exception( 'All related jobs must be cleared before setting Destroyed' )

    self.built_at = None
    self.config_uuid = str( uuid.uuid4() )  # new on destroyed, that way we can leave anything that might still be kicking arround in the dust
    self.full_clean()
    self.save()
    for dependency in self.dependant_dependencies:
      dependency.setDestroyed()

  def configAttributes( self ):
    provisioning_interface = self.provisioning_interface
    provisioning_address = self.provisioning_address
    primary_interface = self.primary_interface
    primary_address = self.primary_address
    result = {
               '_structure_id': self.pk,
               '_structure_state': self.state,
               '_structure_config_uuid': self.config_uuid,
               '_hostname': self.hostname,
               '_domain_name': self.domain_name,
               '_fqdn': self.fqdn,
               '_provisioning_interface': provisioning_interface.physical_location if provisioning_interface is not None else None,
               '_provisioning_interface_mac': provisioning_interface.mac if provisioning_interface is not None else None,
               '_provisioning_address': provisioning_address.as_dict if provisioning_address is not None else None,
               '_primary_interface': primary_interface.name if primary_interface is not None else None,
               '_primary_interface_mac': primary_interface.mac if primary_interface is not None else None,
               '_primary_address': primary_address.as_dict if primary_address is not None else None,
               '_interface_map': {}
             }

    for iface in self.foundation.networkinterface_set.all():  # mabey? mabey not?
      result[ '_interface_map' ][ iface.name ] = iface.config

    return result

  @property
  def state( self ):
    if self.built_at is not None:
      return 'built'

    return 'planned'

  @property
  def can_delete( self ):
    return self.state != 'build'

  @property
  def description( self ):
    return self.hostname

  @property
  def dependant_dependencies( self ):
    try:
      return Dependency.objects.filter( structure=self )
    except Dependency.DoesNotExist:
      return []

  @property
  def dependencyId( self ):
    return 's-{0}'.format( self.pk )

  @cinp.action( return_type='Integer', paramater_type_list=[ '_USER_' ] )
  def doCreate( self, user ):
    from contractor.Foreman.lib import createJob
    return createJob( 'create', self, user )

  @cinp.action( return_type='Integer', paramater_type_list=[ '_USER_' ] )
  def doDestroy( self, user ):
    from contractor.Foreman.lib import createJob
    return createJob( 'destroy', self, user )

  @cinp.action( return_type='Integer', paramater_type_list=[ '_USER_', 'String' ]  )
  def doJob( self, user, name ):
    from contractor.Foreman.lib import createJob
    if name in ( 'create', 'destroy' ):
      raise ValueError( 'Invalid Job Name' )

    return createJob( name, self, user )

  @cinp.action( return_type={ 'type': 'Model', 'model': 'contractor.Foreman.models.StructureJob' }  )
  def getJob( self ):
    try:
      return self.structurejob
    except ObjectDoesNotExist:
      pass

    return None

  @cinp.action( return_type='Map' )
  def getConfig( self ):
    return mergeValues( getConfig( self ) )

  @cinp.action( return_type='Map', paramater_type_list=[ 'Map' ] )
  def updateConfig( self, config_value_map ):  # TODO: this is a bad Idea, need to figure out a better way to do this, at least restrict it to accounts that can create/updatre structures
    self.config_values.update( config_value_map )
    self.full_clean()
    self.save()

    return mergeValues( getConfig( self ) )

  @cinp.list_filter( name='site', paramater_type_list=[ { 'type': 'Model', 'model': Site } ] )
  @staticmethod
  def filter_site( site ):
    return Structure.objects.filter( site=site )

  @cinp.list_filter( name='complex', paramater_type_list=[ { 'type': 'Model', 'model': 'contractor.Building.models.Complex' } ] )
  @staticmethod
  def filter_complex( complex ):
    return complex.members.all()

  @cinp.check_auth()
  @staticmethod
  def checkAuth( user, verb, id_list, action=None ):
    return True

  def clean( self, *args, **kwargs ):
    super().clean( *args, **kwargs )
    errors = {}

    if self.foundation_id is not None and self.foundation.blueprint not in self.blueprint.combined_foundation_blueprint_list:
      errors[ 'foundation' ] = 'The blueprint "{0}" is not allowed on foundation "{1}"'.format( self.blueprint, self.foundation.blueprint )

    if self.config_values is not None:
      for name in self.config_values:
        if not config_name_regex.match( name ):
          errors[ 'config_values' ] = 'config item name "{0}" is invalid'.format( name )
          break

    if errors:
      raise ValidationError( errors )

  def delete( self ):
    if not self.can_delete:
      raise models.ProtectedError( 'Structure not Deleteable', self )

    super().delete()

  def __str__( self ):
    return 'Structure #{0}({1}) in "{2}"'.format( self.pk, self.hostname, self.site.pk )
Beispiel #6
0
 class testModel( models.Model ):
   f = MapField( **kwargs )
Beispiel #7
0
 class testModel( models.Model ):
   f = MapField( default={ 'b': 2 }, blank=True )
Beispiel #8
0
 class testModel2( models.Model ):
   f = MapField()
Beispiel #9
0
 class testModel( models.Model ):
   f = MapField( default={ 'b': 2 } )
Beispiel #10
0
 class testModel( models.Model ):
   f = MapField( blank=True )
Beispiel #11
0
 class testModel( models.Model ):
   f = MapField( default=None, null=True, blank=True )
Beispiel #12
0
class Foundation( models.Model ):
  site = models.ForeignKey( Site, on_delete=models.PROTECT )           # ie where to build it
  blueprint = models.ForeignKey( FoundationBluePrint, on_delete=models.PROTECT )
  locator = models.CharField( max_length=100, unique=True )
  config_values = MapField( blank=True )
  id_map = JSONField( blank=True )  # ie a dict of asset, chassis, system, etc types
  interfaces = models.ManyToManyField( RealNetworkInterface, through='FoundationNetworkInterface' )
  located_at = models.DateTimeField( editable=False, blank=True, null=True )
  built_at = models.DateTimeField( editable=False, blank=True, null=True )
  updated = models.DateTimeField( editable=False, auto_now=True )
  created = models.DateTimeField( editable=False, auto_now_add=True )

  def setLocated( self ):
    try:
      self.structure.setDestroyed()  # TODO: this may be a little harsh
    except Structure.DoesNotExist:
      pass
    self.located_at = timezone.now()
    self.built_at = None
    self.save()

  def setBuilt( self ):
    if self.located_at is None:
      self.located_at = timezone.now()
    self.built_at = timezone.now()
    self.save()

  def setDestroyed( self ):
    try:
      self.structure.setDestroyed()  # TODO: this may be a little harsh
    except Structure.DoesNotExist:
      pass
    self.built_at = None
    self.located_at = None
    self.save()

  @staticmethod
  def getTscriptValues( write_mode=False ):  # locator is handled seperatly
    return {  # none of these base items are writeable, ignore the write_mode for now
              'id': ( lambda foundation: foundation.pk, None ),
              'type': ( lambda foundation: foundation.subclass.type, None ),
              'site': ( lambda foundation: foundation.site.pk, None ),
              'blueprint': ( lambda foundation: foundation.blueprint.pk, None ),
              'id_map': ( lambda foundation: foundation.ip_map, None ),
              'interface_list': ( lambda foundation: [ i for i in foundation.interfaces.all() ], None )
            }

  @staticmethod
  def getTscriptFunctions():
    return {}

  def configValues( self ):
    return {
              'foundation_id': self.pk,
              'foundation_type': self.type,
              'foundation_state': self.state,
              'foundation_class_list': self.class_list
            }

  @property
  def subclass( self ):
    for attr in FOUNDATION_SUBCLASS_LIST:
      try:
        return getattr( self, attr )
      except AttributeError:
        pass

    return self

  @cinp.action( 'String' )
  def getRealFoundationURI( self ):  # TODO: this is such a hack, figure  out a better way
    subclass = self.subclass
    class_name = type( subclass ).__name__
    if class_name == 'Foundation':
      return '/api/v1/Building/Foundation:{0}:'.format( subclass.pk )

    elif class_name == 'VirtualBoxFoundation':
      return '/api/v1/VirtualBox/VirtualBoxFoundation:{0}:'.format( subclass.pk )

    elif class_name == 'ManualFoundation':
      return '/api/v1/Manual/ManualFoundation:{0}:'.format( subclass.pk )

    raise ValueError( 'Unknown Foundation class "{0}"'.format( class_name ) )

  @cinp.action( 'Map' )
  def getConfig( self ):
    return getConfig( self.subclass )

  @property
  def type( self ):
    return 'Unknown'

  @property
  def class_list( self ):
    # top level generic classes: Metal, VM, Container, Switch, PDU
    return []

  @property
  def can_auto_locate( self ):  # child models can decide if it can auto submit job for building, ie: vm (and like foundations) are only canBuild if their structure is auto_build
    return False

  @property
  def state( self ):
    if self.located_at is not None and self.built_at is not None:
      return 'built'

    elif self.located_at is not None:
      return 'located'

    return 'planned'

  @cinp.list_filter( name='site', paramater_type_list=[ { 'type': 'Model', 'model': 'contractor.Site.models.Site' } ] )
  @staticmethod
  def filter_site( site ):
    return Foundation.objects.filter( site=site )

  @cinp.check_auth()
  @staticmethod
  def checkAuth( user, method, id_list, action=None ):
    return True

  def clean( self, *args, **kwargs ):
    super().clean( *args, **kwargs )
    errors = {}
    if self.type not in self.blueprint.foundation_type_list:
      errors[ 'name' ] = 'Blueprint "{0}" does not list this type ({1})'.format( self.blueprint.description, self.type )

    if errors:
      raise ValidationError( errors )

  def __str__( self ):
    return 'Foundation #{0} of "{1}" in "{2}"'.format( self.pk, self.blueprint.pk, self.site.pk )
Beispiel #13
0
class Structure( Networked ):
  blueprint = models.ForeignKey( StructureBluePrint, on_delete=models.PROTECT )  # ie what to bild
  foundation = models.OneToOneField( Foundation, on_delete=models.PROTECT )      # ie what to build it on
  config_uuid = models.CharField( max_length=36, default=getUUID, unique=True )  # unique
  config_values = MapField( blank=True )
  auto_build = models.BooleanField( default=True )
  build_priority = models.IntegerField( default=100 )
  built_at = models.DateTimeField( editable=False, blank=True, null=True )
  updated = models.DateTimeField( editable=False, auto_now=True )
  created = models.DateTimeField( editable=False, auto_now_add=True )

  def setBuilt( self ):
    self.built_at = timezone.now()
    self.save()

  def setDestroyed( self ):
    self.built_at = None
    self.config_uuid = str( uuid.uuid4() )  # new on destroyed, that way we can leave anything that might still be kicking arround in the dust
    self.save()
    for dependancy in self.dependancy_set.all():
      dependancy.setDestroyed()

  def configValues( self ):
    return {
             'hostname': self.hostname,
             'structure': self.pk,
             'state': self.state,
             'config_uuid': self.config_uuid
           }

  @cinp.action( 'Map' )
  def getConfig( self ):
    return getConfig( self )

  @property
  def state( self ):
    if self.built_at is not None:
      return 'built'

    return 'planned'

  @cinp.list_filter( name='site', paramater_type_list=[ { 'type': 'Model', 'model': 'contractor.Site.models.Site' } ] )
  @staticmethod
  def filter_site( site ):
    return Structure.objects.filter( site=site )

  @cinp.check_auth()
  @staticmethod
  def checkAuth( user, method, id_list, action=None ):
    return True

  def clean( self, *args, **kwargs ):
    super().clean( *args, **kwargs )
    errors = {}
    if self.foundation.blueprint not in self.blueprint.combined_foundation_blueprint_list:
      errors[ 'foundation' ] = 'The blueprint "{0}" is not allowed on foundation "{1}"'.format( self.blueprint.description, self.foundation.blueprint.description )

    if errors:
      raise ValidationError( errors )

  def __str__( self ):
    return 'Structure #{0} of "{1}" in "{2}"'.format( self.pk, self.blueprint.pk, self.site.pk )