text: import logging
  
  from django.db.models import Count
  from django.http import (
      HttpResponse,
      HttpResponseBadRequest,
      HttpResponseNotFound,
      HttpResponseNotModified
  )
  from django.utils.http import http_date, parse_http_date_safe
  from django.views.generic.base import TemplateView
  from rest_framework.views import APIView
  
  from ralph.admin.helpers import get_client_ip
  from ralph.assets.models.components import Ethernet
  from ralph.data_center.models import DataCenter
  from ralph.deployment.models import Deployment
  from ralph.dhcp.models import DHCPEntry, DHCPServer
  from ralph.networks.models.networks import (
      IPAddress,
      Network,
      NetworkEnvironment
  )
  
  logger = logging.getLogger(__name__)
  
  
  def last_modified_date(qs, filter_dict=None):
      last_date = None
      if filter_dict is None:
          filter_dict = {}
      obj = qs.filter(**filter_dict).order_by('-modified').first()
      if obj:
          last_date = obj.modified
      return last_date
  
  
  class LastModifiedMixin(object):
      """Add last modified to HTTP response if last_modified attr is exist."""
  
      @property
      def last_timestamp(self):
          last_modified = getattr(self, 'last_modified', None)
          return last_modified and int(last_modified.timestamp())
  
      def is_modified(self, request):
          http_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
          if http_modified_since is None or self.last_timestamp is None:
              return True
          return self.last_timestamp > parse_http_date_safe(http_modified_since)
  
      def dispatch(self, request, *args, **kwargs):
          response = super().dispatch(request, *args, **kwargs)
          if not self.is_modified(request):
              return HttpResponseNotModified()
          response['Last-Modified'] = http_date(self.last_timestamp)
          return response
  
  
  class DHCPConfigMixin(object):
      content_type = 'text/plain'
  
      @staticmethod
      def check_objects_existence_by_names(model_class, names):
          found = model_class.objects.filter(name__in=names)
          not_found = set(names) - set([obj.name for obj in found])
          return found, not_found
  
      def dispatch(self, request, *args, **kwargs):
          dc_names = request.GET.getlist('dc', None)
          env_names = request.GET.getlist('env', None)
          if dc_names and env_names:
              return HttpResponseBadRequest(
                  'Only DC or ENV mode available.',
                  content_type=self.content_type
              )
  
          if not (dc_names or env_names):
              return HttpResponseBadRequest(
                  'Please specify DC or ENV.',
                  content_type=self.content_type
              )
  
          if dc_names:
              found, not_found = self.check_objects_existence_by_names(
                  DataCenter, dc_names
              )
              if not_found:
                  return HttpResponseNotFound(
                      'DC: {} doesn\'t exists.'.format(', '.join(not_found)),
                      content_type='text/plain'
                  )
  
              environments = NetworkEnvironment.objects.filter(
                  data_center__in=found
              )
          elif env_names:
              found, not_found = self.check_objects_existence_by_names(
                  NetworkEnvironment, env_names
              )
              if not_found:
                  return HttpResponseNotFound(
                      'ENV: {} doesn\'t exists.'.format(', '.join(not_found)),
                      content_type='text/plain'
                  )
              environments = found
          self.networks = Network.objects.select_related(
              'network_environment'
          ).filter(
              network_environment__in=environments,
              dhcp_broadcast=True,
          )
          self.last_modified = self.get_last_modified(self.networks)
          return super().dispatch(request, *args, **kwargs)
  
  
  class DHCPSyncView(APIView):
      def get(self, request, *args, **kwargs):
          ip = get_client_ip(request)
          logger.info('Sync request DHCP server with IP: %s', ip)
          if not DHCPServer.update_last_synchronized(ip):
              return HttpResponseNotFound(
                  'DHCP server doesn\'t exist.', content_type='text/plain'
              )
          return HttpResponse('OK', content_type='text/plain')
  
  
  class DHCPEntriesView(
      DHCPConfigMixin, LastModifiedMixin, TemplateView, APIView
  ):
      http_method_names = ['get']
      template_name = 'dhcp/entries.conf'
  
      def get_last_modified(self, networks):
          """
          Return the latest date based on ``modified`` field from networks,
          IP (DHCP entry), ethernet.
          """
          last_items = []
  
          try:
              last_items.append(Deployment.objects.latest('modified').modified)
          except Deployment.DoesNotExist:
              pass
          last_items.append(last_modified_date(networks))
          last_items.append(
              last_modified_date(DHCPEntry.objects, filter_dict={
                  'network__in': networks
              })
          )
          last_items.append(
              last_modified_date(Ethernet.objects, filter_dict={
                  'ipaddress__network__in': networks
              })
          )
          last_items.append(
              last_modified_date(IPAddress.objects, filter_dict={
                  'network__in': networks
              })
          )
          last_items = [item for item in last_items if item is not None]
          if not last_items:
              return None
          return max(last_items)
  
      def _filter_dhcp_entries(self, entries):
          """
          Exclude entries with duplicated hostnames.
          """
          duplicated_hostnames = [e['hostname'] for e in entries.values(
              'hostname'
          ).annotate(c=Count('id')).filter(c__gt=1)]
          for hostname in duplicated_hostnames:
              logger.error(
                  'Duplicated hostname for DHCP entry: {}'.format(hostname)
              )
          return entries.exclude(hostname__in=duplicated_hostnames)
  
      def _get_dhcp_entries(self, networks):
          """
          Returns filtered DHCP entries for given networks.
          """
          return self._filter_dhcp_entries(DHCPEntry.objects.filter(
              network__in=networks
          ).order_by('hostname'))
  
      def get_context_data(self, **kwargs):
          context = super().get_context_data(**kwargs)
          context.update({
              'last_modified': self.last_modified,
              'entries': self._get_dhcp_entries(self.networks),
          })
          return context
  
  
  class DHCPNetworksView(
      DHCPConfigMixin, LastModifiedMixin, TemplateView, APIView
  ):
      template_name = 'dhcp/networks.conf'
  
      def get_last_modified(self, networks):
          last_items = []
          last_items.append(last_modified_date(networks))
          last_items.append(
              last_modified_date(NetworkEnvironment.objects, filter_dict={
                  'network__in': networks
              })
          )
          last_items.append(
              last_modified_date(IPAddress.objects, filter_dict={
                  'network__in': networks,
                  'is_gateway': True
              })
          )
          last_items = [item for item in last_items if item is not None]
          if not last_items:
              return None
          return max(last_items)
  
      def get_context_data(self, **kwargs):
          context = super().get_context_data(**kwargs)
          networks = self.networks.filter(
              network_environment__domain__isnull=False,
              dhcp_broadcast=True,
              gateway__isnull=False,
          ).exclude(
              network_environment=False
          ).prefetch_related('dns_servers')
          context.update({
              'last_modified': self.last_modified,
              'entries': networks,
          })
          return context
  