Esempio n. 1
0
 def test_contains_long_hex_number(self):
   """Tests contains_long_hex_number()."""
   self.assertFalse(utilities.contains_long_hex_number(''))
   self.assertFalse(utilities.contains_long_hex_number('cluster-insight'))
   self.assertFalse(utilities.contains_long_hex_number('abc-123'))
   self.assertFalse(utilities.contains_long_hex_number(u'hello01234567'))
   self.assertTrue(utilities.contains_long_hex_number(u'hello.01234567'))
   self.assertTrue(utilities.contains_long_hex_number(
       'k8s_php-redis.b317029a_guestbook-controller-ls6k1.default.api_'
       'f991d53e-b949-11e4-8246-42010af0c3dd_8dcdfec8'))
Esempio n. 2
0
def get_containers(gs, docker_host):
  """Gets the list of all containers in 'docker_host'.

  Args:
    gs: global state.
    docker_host: the Docker host running the containers.

  Returns:
    list of wrapped container objects.
    Each element in the list is the result of
    utilities.wrap_object(container, 'Container', ...)

  Raises:
    CollectorError: in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
  containers, timestamp = gs.get_containers_cache().lookup(docker_host)
  if timestamp is not None:
    gs.logger_info(
        'get_containers(docker_host=%s) cache hit returns '
        '%d containers', docker_host, len(containers))
    return containers

  url = 'http://{docker_host}:{port}/containers/json'.format(
      docker_host=docker_host, port=gs.get_docker_port())
  # A typical value of 'docker_host' is:
  # k8s-guestbook-node-3.c.rising-apricot-840.internal
  # Use only the first period-seperated element for the test file name.
  fname = '{host}-containers'.format(host=docker_host.split('.')[0])
  try:
    containers_list = fetch_data(gs, url, fname)
  except collector_error.CollectorError:
    raise
  except:
    msg = ('fetching %s or %s failed with exception %s' %
           (url, fname, sys.exc_info()[0]))
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  if not isinstance(containers_list, types.ListType):
    msg = 'invalid response from fetching %s' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  containers = []
  timestamps = []
  for container_info in containers_list:
    # NOTE: container 'Name' is stable across container re-starts whereas
    # container 'Id' is not.
    # This may be because Kubernertes assigns the Name while Docker assigns
    # the Id (?)
    # The container Name is the only element of the array 'Names' -
    # why is Names an array here?
    # skip the leading / in the Name
    if not (isinstance(container_info.get('Names'), types.ListType) and
            container_info['Names'] and
            utilities.valid_string(container_info['Names'][0]) and
            container_info['Names'][0][0] == '/'):
      msg = 'invalid containers data format. docker_host=%s' % docker_host
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    container_id = container_info['Names'][0][1:]
    container, ts = _inspect_container(gs, docker_host, container_id)
    if container is None:
      continue

    if not utilities.valid_string(container.get('Name')):
      msg = ('missing or invalid "Name" attribute in container %s' %
             container_id)
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    if container['Name'] != ('/' + container_id):
      msg = ('container %s\'s Name attribute is "%s"; expecting "%s"' %
             (container_id, container['Name'], '/' + container_id))
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    # The 'container_id' is most often unique, because it contains long
    # unique hex numbers. However, in some cases the 'container_id' is simply
    # the image name, such as "cluster-insight". In this case 'container_id'
    # is not unique in the context graph, so we make it unique by appending
    # the a prefix of the Docker ID of the container.
    hex_id = utilities.object_to_hex_id(container)
    if hex_id is None:
      msg = 'Could not compute short hex ID of container %s' % container_id
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    if utilities.contains_long_hex_number(container_id):
      short_label = hex_id
      unique_id = container_id
    else:
      # The short label is descriptive when 'container_id' does not contain
      # long hex numbers.
      short_label = container_id
      unique_id = '{container_id}-{hex_id}'.format(
          container_id=container_id, hex_id=hex_id)

    wrapped_container = utilities.wrap_object(
        container, 'Container', unique_id, ts, label=short_label)
    containers.append(wrapped_container)
    timestamps.append(ts)

    # If the container's label does not contain long hex fields, it is
    # good enough. It should not be replaced with anything else.
    if not utilities.contains_long_hex_number(short_label):
      continue

    # Modify the container's label after the wrapped container was added
    # to the containers list.
    # Compute the container's short name to create a better container label:
    # short_container_name/short_hex_id.
    # For example: "cassandra/d85b599c17d8".
    parent_pod_id = utilities.get_parent_pod_id(wrapped_container)
    if parent_pod_id is None:
      continue
    parent_pod = kubernetes.get_one_pod(gs, docker_host, parent_pod_id)
    if parent_pod is None:
      continue
    short_container_name = utilities.get_short_container_name(
        wrapped_container, parent_pod)
    if not utilities.valid_string(short_container_name):
      continue
    wrapped_container['annotations']['label'] = (short_container_name + '/' +
                                                 hex_id)

  ret_value = gs.get_containers_cache().update(
      docker_host, containers,
      min(timestamps) if timestamps else time.time())
  gs.logger_info(
      'get_containers(docker_host=%s) returns %d containers',
      docker_host, len(containers))
  return ret_value