def get_image(gs, docker_host, container):
  """Gets the information of the given image in the given host.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.
    container: the container which runs the image.

  Returns:
    If image was found, returns the wrapped image object, which is the result of
    utilities.wrap_object(image, 'Image', ...)
    If the image was not found, returns None.

  Raises:
    CollectorError: in case of failure to fetch data from Docker.
    ValueError: in case the container does not contain a valid image ID.
    Other exceptions may be raised due to exectution errors.
  """
  assert utilities.is_wrapped_object(container, 'Container')
  # The 'image_id' should be a long hexadecimal string.
  image_id = utilities.get_attribute(container, ['properties', 'Image'])
  if not utilities.valid_hex_id(image_id):
    msg = 'missing or invalid image ID in container ID=%s' % container['id']
    gs.logger_error(msg)
    raise ValueError(msg)

  # The 'image_name' should be a symbolic name (not a hexadecimal string).
  image_name = utilities.get_attribute(
      container, ['properties', 'Config', 'Image'])

  if ((not utilities.valid_string(image_name)) or
      utilities.valid_hex_id(image_name)):
    msg = 'missing or invalid image name in container ID=%s' % container['id']
    gs.logger_error(msg)
    raise ValueError(msg)

  cache_key = '%s|%s' % (docker_host, image_id)
  image, timestamp_secs = gs.get_images_cache().lookup(cache_key)
  if timestamp_secs is not None:
    gs.logger_info('get_image(docker_host=%s, image_id=%s) cache hit',
                   docker_host, image_id)
    return image

  # 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.
  # The typical value of 'image_name' is:
  # brendanburns/php-redis
  # We convert embedded '/' and ':' characters to '-' to avoid interference with
  # the directory structure or file system.
  url = 'http://{docker_host}:{port}/images/{image_id}/json'.format(
      docker_host=docker_host, port=gs.get_docker_port(), image_id=image_id)
  fname = '{host}-image-{id}'.format(
      host=docker_host.split('.')[0],
      id=image_name.replace('/', '-').replace(':', '-'))

  try:
    image = fetch_data(gs, url, fname, expect_missing=True)
  except ValueError:
    # image not found.
    msg = 'image not found for image_id: %s' % image_id
    gs.logger_info(msg)
    return None
  except collector_error.CollectorError:
    raise
  except:
    msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0])
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  now = time.time()
  # compute the two labels of the image.
  # The first is a 12-digit hexadecimal number shown by "docker images".
  # The second is the symbolic name of the image.
  full_hex_label = image.get('Id')
  if not (isinstance(full_hex_label, types.StringTypes) and full_hex_label):
    msg = 'Image id=%s has an invalid "Id" attribute value' % image_id
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  short_hex_label = utilities.object_to_hex_id(image)
  if short_hex_label is None:
    msg = 'Could not compute short hex ID of image %s' % image_id
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  wrapped_image = utilities.wrap_object(
      image, 'Image', full_hex_label, now,
      label=short_hex_label, alt_label=image_name)

  ret_value = gs.get_images_cache().update(cache_key, wrapped_image, now)
  gs.logger_info('get_image(docker_host=%s, image_id=%s, image_name=%s)',
                 docker_host, image_id, image_name)
  return ret_value
Exemple #2
0
def get_version(gs):
  """Returns a human-readable information of the currently running image.

  Args:
    gs: global state.

  Returns:
  A string of the form:
  <symbolic container name> <container hex ID> <creation date and time>

  Raises:
    CollectorError: in case of any error to compute the running image
      information.
  """
  version, timestamp_secs = gs.get_version_cache().lookup('')
  if timestamp_secs is not None:
    assert utilities.valid_string(version)
    gs.logger_info('get_version() cache hit')
    return version

  if gs.get_testing():
    fname = 'testdata/proc-self-cgroup.txt'
  else:
    fname = '/proc/self/cgroup'

  try:
    f = open(fname, 'r')
    cgroup = f.read()
    f.close()
  except IOError:
    # file not found
    msg = 'failed to open or read %s' % fname
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)
  except:
    msg = 'reading %s failed with exception %s' % (fname, sys.exc_info()[0])
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  # The file must contain an entry for '\d+:cpu:/...'.
  m = re.search(r'\b\d+:cpu:/([0-9a-fA-F]+)\b', cgroup)
  if not m:
    msg = 'could not find an entry for "cpu:/docker/..." in %s' % fname
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  hex_container_id = m.group(1)
  if gs.get_testing():
    # This pod name is guaranteed to match a pod in the testdata directory.
    my_pod_name = 'kube-dns-bqw5e'
  else:
    my_pod_name = os.uname()[1]
  assert utilities.valid_string(my_pod_name)

  # Find my node name from my pod.
  my_node_name = None
  for pod in kubernetes.get_pods(gs):
    assert utilities.is_wrapped_object(pod, 'Pod')
    if pod['id'] == my_pod_name:
      my_node_name = utilities.get_attribute(
          pod, ['properties', 'spec', 'nodeName'])
      break

  if not utilities.valid_string(my_node_name):
    msg = ('could not find pod %s or this pod does not contain a valid '
           'node name' % my_pod_name)
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  # inspect the running container.
  # Must specify an explicit host name (not "localhost").
  url = 'http://{host}:{port}/containers/{container_id}/json'.format(
      host=my_node_name, port=gs.get_docker_port(),
      container_id=hex_container_id)
  container = fetch_data(gs, url, 'container-' + hex_container_id[:12])

  # Fetch the image symbolic name and hex ID from the container information.
  symbolic_image_id = utilities.get_attribute(container, ['Config', 'Image'])
  hex_image_id = utilities.get_attribute(container, ['Image'])

  # Verify the image symbolic name and the image hex ID.
  if not (utilities.valid_string(symbolic_image_id) and
          not utilities.valid_hex_id(symbolic_image_id) and
          utilities.valid_hex_id(hex_image_id)):
    msg = 'could not find or invalid image information in container %s' % url
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  # Fetch image information.
  # Must specify an explicit host name (not "localhost").
  url = 'http://{host}:{port}/images/{image_id}/json'.format(
      host=my_node_name, port=gs.get_docker_port(),
      image_id=hex_image_id)
  image = fetch_data(gs, url, 'image-' + hex_image_id[:12])

  # Fetch the image creation timestamp.
  created = utilities.get_attribute(image, ['Created'])
  if not utilities.valid_string(created):
    msg = 'could not find image creation timestamp in %s' % url
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  # Remove the trailing subsecond part of the creation timestamp.
  created = re.sub(r'\.[0-9]+Z$', '', created)

  version = '%s %s %s' % (symbolic_image_id, hex_image_id[:12], created)
  ret_value = gs.get_version_cache().update('', version)
  gs.logger_info('get_version() returns: %s', ret_value)
  return ret_value
def get_version(gs):
  """Returns a human-readable information of the currently running image.

  Args:
    gs: global state.

  Returns:
  A string of the form:
  <symbolic container name> <container hex ID> <creation date and time>

  Raises:
    CollectorError: in case of any error to compute the running image
      information.
  """
  version, timestamp_secs = gs.get_version_cache().lookup('')
  if timestamp_secs is not None:
    assert utilities.valid_string(version)
    gs.logger_info('get_version() cache hit')
    return version

  if gs.get_testing():
    fname = 'testdata/proc-self-cgroup.txt'
  else:
    fname = '/proc/self/cgroup'

  try:
    f = open(fname, 'r')
    cgroup = f.read()
    f.close()
  except IOError:
    # file not found
    msg = 'failed to open or read %s' % fname
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)
  except:
    msg = 'reading %s failed with exception %s' % (fname, sys.exc_info()[0])
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  # The file must contain an entry for 'cpu:/docker/...'.
  m = re.search(r'\b[0-9]+:cpu:/docker/([0-9a-fA-F]+)\b', cgroup)
  if not m:
    msg = 'could not find an entry for "cpu:/docker/..." in %s' % fname
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  hex_container_id = m.group(1)
  # inspect the running container.
  url = 'http://localhost:{port}/containers/{container_id}/json'.format(
      port=gs.get_docker_port(), container_id=hex_container_id)
  container = fetch_data(gs, url, 'container-' + hex_container_id[:12])

  # Fetch the image symbolic name and hex ID from the container information.
  symbolic_image_id = utilities.get_attribute(container, ['Config', 'Image'])
  hex_image_id = utilities.get_attribute(container, ['Image'])

  # Verify the image symbolic name and the image hex ID.
  if not (utilities.valid_string(symbolic_image_id) and
          not utilities.valid_hex_id(symbolic_image_id) and
          utilities.valid_hex_id(hex_image_id)):
    msg = 'could not find or invalid image information in container %s' % url
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  # Fetch image information.
  url = 'http://localhost:{port}/images/{image_id}/json'.format(
      port=gs.get_docker_port(), image_id=hex_image_id)
  image = fetch_data(gs, url, 'image-' + hex_image_id[:12])

  # Fetch the image creation timestamp.
  created = utilities.get_attribute(image, ['Created'])
  if not utilities.valid_string(created):
    msg = 'could not find image creation timestamp in %s' % url
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  # Remove the trailing subsecond part of the creation timestamp.
  created = re.sub(r'\.[0-9]+Z$', '', created)

  version = '%s %s %s' % (symbolic_image_id, hex_image_id[:12], created)
  ret_value = gs.get_version_cache().update('', version)
  gs.logger_info('get_version() returns: %s', ret_value)
  return ret_value
def get_image(gs, docker_host, container):
    """Gets the information of the given image in the given host.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.
    container: the container which runs the image.

  Returns:
    If image was found, returns the wrapped image object, which is the result of
    utilities.wrap_object(image, 'Image', ...)
    If the image was not found, returns None.

  Raises:
    CollectorError: in case of failure to fetch data from Docker.
    ValueError: in case the container does not contain a valid image ID.
    Other exceptions may be raised due to exectution errors.
  """
    assert utilities.is_wrapped_object(container, 'Container')
    # The 'image_id' should be a long hexadecimal string.
    image_id = utilities.get_attribute(container, ['properties', 'Image'])
    if not utilities.valid_hex_id(image_id):
        msg = 'missing or invalid image ID in container ID=%s' % container['id']
        gs.logger_error(msg)
        raise ValueError(msg)

    # The 'image_name' should be a symbolic name (not a hexadecimal string).
    image_name = utilities.get_attribute(container,
                                         ['properties', 'Config', 'Image'])

    if ((not utilities.valid_string(image_name))
            or utilities.valid_hex_id(image_name)):
        msg = 'missing or invalid image name in container ID=%s' % container[
            'id']
        gs.logger_error(msg)
        raise ValueError(msg)

    cache_key = '%s|%s' % (docker_host, image_id)
    image, timestamp_secs = gs.get_images_cache().lookup(cache_key)
    if timestamp_secs is not None:
        gs.logger_info('get_image(docker_host=%s, image_id=%s) cache hit',
                       docker_host, image_id)
        return image

    # 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.
    # The typical value of 'image_name' is:
    # brendanburns/php-redis
    # We convert embedded '/' and ':' characters to '-' to avoid interference with
    # the directory structure or file system.
    url = 'http://{docker_host}:{port}/images/{image_id}/json'.format(
        docker_host=docker_host, port=gs.get_docker_port(), image_id=image_id)
    fname = '{host}-image-{id}'.format(host=docker_host.split('.')[0],
                                       id=image_name.replace('/', '-').replace(
                                           ':', '-'))

    try:
        image = fetch_data(gs, url, fname, expect_missing=True)
    except ValueError:
        # image not found.
        msg = 'image not found for image_id: %s' % image_id
        gs.logger_info(msg)
        return None
    except collector_error.CollectorError:
        raise
    except:
        msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0])
        gs.logger_exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    # compute the two labels of the image.
    # The first is a 12-digit hexadecimal number shown by "docker images".
    # The second is the symbolic name of the image.
    full_hex_label = image.get('Id')
    if not (isinstance(full_hex_label, types.StringTypes) and full_hex_label):
        msg = 'Image id=%s has an invalid "Id" attribute value' % image_id
        gs.logger_error(msg)
        raise collector_error.CollectorError(msg)

    short_hex_label = utilities.object_to_hex_id(image)
    if short_hex_label is None:
        msg = 'Could not compute short hex ID of image %s' % image_id
        gs.logger_error(msg)
        raise collector_error.CollectorError(msg)

    wrapped_image = utilities.wrap_object(image,
                                          'Image',
                                          full_hex_label,
                                          now,
                                          label=short_hex_label,
                                          alt_label=image_name)

    ret_value = gs.get_images_cache().update(cache_key, wrapped_image, now)
    gs.logger_info('get_image(docker_host=%s, image_id=%s, image_name=%s)',
                   docker_host, image_id, image_name)
    return ret_value