コード例 #1
0
  def best_label(self, obj):
    """Returns the best human-readable label of the given object.

    We perfer the "alternateLabel" over "label" and a string not composed
    of only hexadecimal digits over hexadecimal digits.

    This function must be called when self._lock is held.

    Args:
      obj: a dictionary containing an "annotations" attribute. The value
        of this attribute should be a dictionary, which may contain
        "alternateLabel" and "Label" attributes.

    Returns:
    The best human-readable label.
    """
    alt_label = utilities.get_attribute(obj, ['annotations', 'alternateLabel'])
    label = utilities.get_attribute(obj, ['annotations', 'label'])
    if (utilities.valid_string(alt_label) and
        re.search('[^0-9a-fA-F]', alt_label)):
      return alt_label
    elif utilities.valid_string(label) and re.search('[^0-9a-fA-F]', label):
      return label
    elif utilities.valid_string(alt_label):
      return alt_label
    elif utilities.valid_string(label):
      return label
    else:
      # should not arrive here.
      return '<unknown>'
コード例 #2
0
    def best_label(self, obj):
        """Returns the best human-readable label of the given object.

    We perfer the "alternateLabel" over "label" and a string not composed
    of only hexadecimal digits over hexadecimal digits.

    This function must be called when self._lock is held.

    Args:
      obj: a dictionary containing an "annotations" attribute. The value
        of this attribute should be a dictionary, which may contain
        "alternateLabel" and "Label" attributes.

    Returns:
    The best human-readable label.
    """
        alt_label = utilities.get_attribute(obj,
                                            ['annotations', 'alternateLabel'])
        label = utilities.get_attribute(obj, ['annotations', 'label'])
        if (utilities.valid_string(alt_label)
                and re.search('[^0-9a-fA-F]', alt_label)):
            return alt_label
        elif utilities.valid_string(label) and re.search(
                '[^0-9a-fA-F]', label):
            return label
        elif utilities.valid_string(alt_label):
            return alt_label
        elif utilities.valid_string(label):
            return label
        else:
            # should not arrive here.
            return '<unknown>'
コード例 #3
0
def get_pods(gs, node_id=None):
  """Gets the list of all pods in the given node or in the cluster.

  When 'node_id' is None, it returns the list of pods in the cluster.
  When 'node_id' is a non-empty string, it returns the list of pods in that
  node.

  Args:
    gs: global state.
    node_id: the parent node of the pods or None.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
  pods_label = '' if node_id is None else node_id
  pods, timestamp_secs = gs.get_pods_cache().lookup(pods_label)
  if timestamp_secs is not None:
    gs.logger_info('get_pods(pods_label=%s) cache hit returns %d pods',
                   pods_label, len(pods))
    return pods

  pods = []
  url = '{kubernetes}/pods'.format(kubernetes=KUBERNETES_API)
  try:
    result = fetch_data(gs, url)
  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()
  if not (isinstance(result, types.DictType) and 'items' in result):
    msg = 'invalid result when fetching %s' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  for pod in result['items']:
    name = utilities.get_attribute(pod, ['metadata', 'name'])
    if not utilities.valid_string(name):
      # an invalid pod without a valid pod ID value.
      continue
    wrapped_pod = utilities.wrap_object(pod, 'Pod', name, now)
    if node_id:
      # pod['spec']['host'] may be missing if the pod is in "Waiting"
      # status.
      if utilities.get_attribute(pod, ['spec', 'host']) == node_id:
        pods.append(wrapped_pod)
    else:
      # append pod to output if 'node_id' is not specified.
      pods.append(wrapped_pod)

  ret_value = gs.get_pods_cache().update(pods_label, pods, now)
  gs.logger_info('get_pods(node_id=%s) returns %d pods', pods_label, len(pods))
  return ret_value
コード例 #4
0
ファイル: metrics.py プロジェクト: vasbala/cluster-insight
def annotate_container(project_id, container, parent_pod):
    """Annotate the given container with Heapster GCM metric information.

  Args:
    project_id: the project ID
    container: the container object to annotate.
    parent_pod: the parent pod of 'container'.

  Raises:
    AssertionError: if the input arguments are invalid or if
    'parent_pod' is not the parent of 'container'
  """
    assert utilities.valid_string(project_id)
    assert utilities.is_wrapped_object(container, "Container")
    assert utilities.is_wrapped_object(parent_pod, "Pod")
    parent_name = utilities.get_attribute(container, ["properties", "Config", "Hostname"])
    assert utilities.valid_string(parent_name)
    pod_name = utilities.get_attribute(parent_pod, ["properties", "metadata", "name"])
    assert utilities.valid_string(pod_name)

    # The 'parent_name' value is truncated to the first 64 characters.
    # Thus it must be the prefix of the full pod name.
    assert pod_name.startswith(parent_name)

    m = _make_gcm_metrics(project_id, _get_container_labels(container, parent_pod))
    if m is None:
        return
    if container.get("annotations") is None:
        container["annotations"] = {}
    container["annotations"]["metrics"] = m
コード例 #5
0
def get_containers_from_pod(pod):
    """Extracts synthesized container resources from a pod.

  Only containers for which status is available are included. (The pod may
  still be pending.)
  """
    assert utilities.is_wrapped_object(pod, "Pod")
    specs = utilities.get_attribute(pod, ["properties", "spec", "containers"])
    statuses = utilities.get_attribute(pod, ["properties", "status", "containerStatuses"])
    timestamp = pod["timestamp"]

    spec_dict = {}
    for spec in specs or []:
        spec = spec.copy()
        spec_dict[spec.pop("name")] = spec

    containers = []
    for status in statuses or []:
        status = status.copy()
        name = status.pop("name")
        unique_id = status.get("containerID", name)
        obj = {"metadata": {"name": name}, "spec": spec_dict.get(name, {}), "status": status}
        container = utilities.wrap_object(obj, "Container", unique_id, timestamp, label=name)
        containers.append(container)

    return containers
コード例 #6
0
def annotate_container(project_id, container, parent_pod):
    """Annotate the given container with Heapster GCM metric information.

  Args:
    project_id: the project ID
    container: the container object to annotate.
    parent_pod: the parent pod of 'container'.

  Raises:
    AssertionError: if the input arguments are invalid or if
    'parent_pod' is not the parent of 'container'
  """
    assert utilities.valid_string(project_id)
    assert utilities.is_wrapped_object(container, 'Container')
    assert utilities.is_wrapped_object(parent_pod, 'Pod')
    parent_name = utilities.get_attribute(container,
                                          ['properties', 'Config', 'Hostname'])
    assert utilities.valid_string(parent_name)
    pod_name = utilities.get_attribute(parent_pod,
                                       ['properties', 'metadata', 'name'])
    assert utilities.valid_string(pod_name)

    # The 'parent_name' value is truncated to the first 64 characters.
    # Thus it must be the prefix of the full pod name.
    assert pod_name.startswith(parent_name)

    m = _make_gcm_metrics(project_id,
                          _get_container_labels(container, parent_pod))
    if m is None:
        return
    if container.get('annotations') is None:
        container['annotations'] = {}
    container['annotations']['metrics'] = m
コード例 #7
0
ファイル: metrics.py プロジェクト: vasbala/cluster-insight
def _get_container_labels(container, parent_pod):
    """Returns key/value pairs identifying all metrics of this container.

  Args:
    container: the container object to annotate.
    parent_pod: the parent pod of 'container'.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
    if not utilities.is_wrapped_object(container, "Container"):
        return None
    if not utilities.is_wrapped_object(parent_pod, "Pod"):
        return None

    pod_id = utilities.get_attribute(parent_pod, ["properties", "metadata", "uid"])
    if not utilities.valid_string(pod_id):
        return None

    hostname = utilities.get_attribute(parent_pod, ["properties", "spec", "host"])
    if not utilities.valid_string(hostname):
        return None

    short_container_name = utilities.get_short_container_name(container, parent_pod)

    if not utilities.valid_string(short_container_name):
        return None

    return {"pod_id": pod_id, "hostname": hostname, "container_name": short_container_name}
コード例 #8
0
    def add_resource(self, rid, annotations, rtype, timestamp, obj):
        """Adds a resource to the context graph."""
        assert utilities.valid_string(rid)
        assert utilities.valid_string(
            utilities.get_attribute(annotations, ['label']))
        assert utilities.valid_string(rtype)
        assert utilities.valid_string(timestamp)
        assert isinstance(obj, types.DictType)

        with self._lock:
            # It is possible that the same resource is referenced by more than one
            # parent. In this case the resource is added only once.
            if rid in self._id_set:
                return

            # Add the resource to the context graph data structure.
            resource = {
                'id': rid,
                'type': rtype,
                'timestamp': timestamp,
                'annotations': copy.deepcopy(annotations)
            }

            if self._version is not None:
                resource['annotations']['createdBy'] = self._version

            resource['properties'] = obj

            self._context_resources.append(resource)
            self._id_set.add(rid)
コード例 #9
0
def _do_compute_service(gs, cluster_guid, service, g):
  assert isinstance(gs, global_state.GlobalState)
  assert utilities.valid_string(cluster_guid)
  assert utilities.is_wrapped_object(service, 'Service')
  assert isinstance(g, ContextGraph)

  service_id = service['id']
  service_guid = 'Service:' + service_id
  g.add_resource(service_guid, service['annotations'], 'Service',
                 service['timestamp'], service['properties'])

  # Cluster contains Service.
  g.add_relation(cluster_guid, service_guid, 'contains')

  # Pods load balanced by this service (use the service['spec', 'selector']
  # key/value pairs to find matching Pods)
  selector = utilities.get_attribute(
      service, ['properties', 'spec', 'selector'])
  if selector:
    if not isinstance(selector, types.DictType):
      msg = 'Service id=%s has an invalid "selector" value' % service_id
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    for pod in kubernetes.get_selected_pods(gs, selector):
      pod_guid = 'Pod:' + pod['id']
      # Service loadBalances Pod
      g.add_relation(service_guid, pod_guid, 'loadBalances')
  else:
    gs.logger_error('Service id=%s has no "selector" attribute', service_id)
コード例 #10
0
def _do_compute_rcontroller(gs, cluster_guid, rcontroller, g):
  assert isinstance(gs, global_state.GlobalState)
  assert utilities.valid_string(cluster_guid)
  assert utilities.is_wrapped_object(rcontroller, 'ReplicationController')
  assert isinstance(g, ContextGraph)

  rcontroller_id = rcontroller['id']
  rcontroller_guid = 'ReplicationController:' + rcontroller_id
  g.add_resource(rcontroller_guid, rcontroller['annotations'],
                 'ReplicationController',
                 rcontroller['timestamp'], rcontroller['properties'])

  # Cluster contains Rcontroller
  g.add_relation(cluster_guid, rcontroller_guid, 'contains')

  # Pods that are monitored by this replication controller.
  # Use the rcontroller['spec']['selector'] key/value pairs to find matching
  # pods.
  selector = utilities.get_attribute(
      rcontroller, ['properties', 'spec', 'selector'])
  if selector:
    if not isinstance(selector, types.DictType):
      msg = ('Rcontroller id=%s has an invalid "replicaSelector" value' %
             rcontroller_id)
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)

    for pod in kubernetes.get_selected_pods(gs, selector):
      pod_guid = 'Pod:' + pod['id']
      # Rcontroller monitors Pod
      g.add_relation(rcontroller_guid, pod_guid, 'monitors')
  else:
    gs.logger_error('Rcontroller id=%s has no "spec.selector" attribute',
                    rcontroller_id)
コード例 #11
0
  def add_resource(self, rid, annotations, rtype, timestamp, obj):
    """Adds a resource to the context graph."""
    assert utilities.valid_string(rid)
    assert utilities.valid_string(utilities.get_attribute(
        annotations, ['label']))
    assert utilities.valid_string(rtype)
    assert utilities.valid_string(timestamp)
    assert isinstance(obj, types.DictType)

    with self._lock:
      # It is possible that the same resource is referenced by more than one
      # parent. In this case the resource is added only once.
      if rid in self._id_set:
        return

      # Add the resource to the context graph data structure.
      resource = {
          'id': rid,
          'type': rtype,
          'timestamp': timestamp,
          'annotations': copy.deepcopy(annotations)
      }

      if self._version is not None:
        resource['annotations']['createdBy'] = self._version

      resource['properties'] = obj

      self._context_resources.append(resource)
      self._id_set.add(rid)
コード例 #12
0
def get_pod_host(gs, pod_id):
  """Gets the host name associated with the given pod.

  Args:
    gs: global state.
    pod_id: the pod name.

  Returns:
    If the pod was found, returns the associated host name.
    If the pod was not found, returns an empty string.

  Raises:
    CollectorError in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
  gs.logger_info('calling get_pod_host(pod_id=%s)', pod_id)
  for pod in get_pods(gs):
    if not utilities.valid_string(pod.get('id')):
      # Found an invalid pod without a pod ID.
      continue

    pod_host = utilities.get_attribute(pod, ['properties', 'spec', 'host'])
    if pod['id'] == pod_id and utilities.valid_string(pod_host):
      # 'pod_host' may be missing if the pod is in "Waiting" state.
      return pod_host

  # Could not find pod.
  return ''
コード例 #13
0
def _do_compute_rcontroller(gs, cluster_guid, rcontroller, g):
    assert isinstance(gs, global_state.GlobalState)
    assert utilities.valid_string(cluster_guid)
    assert utilities.is_wrapped_object(rcontroller, 'ReplicationController')
    assert isinstance(g, ContextGraph)

    rcontroller_id = rcontroller['id']
    rcontroller_guid = 'ReplicationController:' + rcontroller_id
    g.add_resource(rcontroller_guid, rcontroller['annotations'],
                   'ReplicationController', rcontroller['timestamp'],
                   rcontroller['properties'])

    # Cluster contains Rcontroller
    g.add_relation(cluster_guid, rcontroller_guid, 'contains')

    # Pods that are monitored by this replication controller.
    # Use the rcontroller['spec']['selector'] key/value pairs to find matching
    # pods.
    selector = utilities.get_attribute(rcontroller,
                                       ['properties', 'spec', 'selector'])
    if selector:
        if not isinstance(selector, types.DictType):
            msg = ('Rcontroller id=%s has an invalid "replicaSelector" value' %
                   rcontroller_id)
            gs.logger_error(msg)
            raise collector_error.CollectorError(msg)

        for pod in kubernetes.get_selected_pods(gs, selector):
            pod_guid = 'Pod:' + pod['id']
            # Rcontroller monitors Pod
            g.add_relation(rcontroller_guid, pod_guid, 'monitors')
    else:
        gs.logger_error('Rcontroller id=%s has no "spec.selector" attribute',
                        rcontroller_id)
コード例 #14
0
def get_pod_host(gs, pod_id):
    """Gets the host name associated with the given pod.

  Args:
    gs: global state.
    pod_id: the pod name.

  Returns:
    If the pod was found, returns the associated host name.
    If the pod was not found, returns an empty string.

  Raises:
    CollectorError in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    gs.logger_info('calling get_pod_host(pod_id=%s)', pod_id)
    for pod in get_pods(gs):
        if not utilities.valid_string(pod.get('id')):
            # Found an invalid pod without a pod ID.
            continue

        pod_host = utilities.get_attribute(pod, ['properties', 'spec', 'host'])
        if pod['id'] == pod_id and utilities.valid_string(pod_host):
            # 'pod_host' may be missing if the pod is in "Waiting" state.
            return pod_host

    # Could not find pod.
    return ''
コード例 #15
0
ファイル: context.py プロジェクト: jackgr/cluster-insight
def _do_compute_pod(gs, cluster_guid, node_guid, pod, g):
  assert isinstance(gs, global_state.GlobalState)
  assert utilities.valid_string(cluster_guid)
  assert utilities.valid_string(node_guid)
  assert utilities.is_wrapped_object(pod, 'Pod')
  assert isinstance(g, ContextGraph)

  pod_id = pod['id']
  pod_guid = 'Pod:' + pod_id
  g.add_resource(pod_guid, pod['annotations'], 'Pod', pod['timestamp'],
                 pod['properties'])

  # pod.properties.spec.nodeName may be missing if the pod is waiting
  # (not running yet).
  docker_host = utilities.get_attribute(
      pod, ['properties', 'spec', 'nodeName'])
  if utilities.valid_string(docker_host):
    # Pod is running.
    if node_guid == ('Node:' + docker_host):
      g.add_relation(node_guid, pod_guid, 'runs')  # Node runs Pod
    else:
      msg = ('Docker host (pod.properties.spec.nodeName)=%s '
             'not matching node ID=%s' % (docker_host, node_guid))
      gs.logger_error(msg)
      raise collector_error.CollectorError(msg)
  else:
    # Pod is not running.
    g.add_relation(cluster_guid, pod_guid, 'contains')  # Cluster contains Pod
コード例 #16
0
def _do_compute_service(gs, cluster_guid, service, g):
    assert isinstance(gs, global_state.GlobalState)
    assert utilities.valid_string(cluster_guid)
    assert utilities.is_wrapped_object(service, 'Service')
    assert isinstance(g, ContextGraph)

    service_id = service['id']
    service_guid = 'Service:' + service_id
    g.add_resource(service_guid, service['annotations'], 'Service',
                   service['timestamp'], service['properties'])

    # Cluster contains Service.
    g.add_relation(cluster_guid, service_guid, 'contains')

    # Pods load balanced by this service (use the service['spec', 'selector']
    # key/value pairs to find matching Pods)
    selector = utilities.get_attribute(service,
                                       ['properties', 'spec', 'selector'])
    if selector:
        if not isinstance(selector, types.DictType):
            msg = 'Service id=%s has an invalid "selector" value' % service_id
            gs.logger_error(msg)
            raise collector_error.CollectorError(msg)

        for pod in kubernetes.get_selected_pods(gs, selector):
            pod_guid = 'Pod:' + pod['id']
            # Service loadBalances Pod
            g.add_relation(service_guid, pod_guid, 'loadBalances')
    else:
        gs.logger_error('Service id=%s has no "selector" attribute',
                        service_id)
コード例 #17
0
ファイル: context.py プロジェクト: jackgr/cluster-insight
def _do_compute_node(gs, input_queue, cluster_guid, node, g):
  assert isinstance(gs, global_state.GlobalState)
  assert isinstance(input_queue, Queue.PriorityQueue)
  assert utilities.valid_string(cluster_guid)
  assert utilities.is_wrapped_object(node, 'Node')
  assert isinstance(g, ContextGraph)

  node_id = node['id']
  node_guid = 'Node:' + node_id
  g.add_resource(node_guid, node['annotations'], 'Node', node['timestamp'],
                 node['properties'])
  g.add_relation(cluster_guid, node_guid, 'contains')  # Cluster contains Node
  # Pods in a Node
  pod_ids = set()
  docker_hosts = set()

  # Process pods sequentially because calls to _do_compute_pod() do not call
  # lower-level services or wait.
  for pod in kubernetes.get_pods(gs, node_id):
    _do_compute_pod(gs, cluster_guid, node_guid, pod, g)
    pod_ids.add(pod['id'])
    # pod.properties.spec.nodeName may be missing if the pod is waiting.
    docker_host = utilities.get_attribute(
        pod, ['properties', 'spec', 'nodeName'])
    if utilities.valid_string(docker_host):
      docker_hosts.add(docker_host)

  # 'docker_hosts' should contain a single Docker host, because all of
  # the pods run in the same Node. However, if it is not the case, we
  # cannot fix the situation, so we just log an error message and continue.
  if len(docker_hosts) != 1:
    gs.logger_error(
        'corrupt pod data in node=%s: '
        '"docker_hosts" is empty or contains more than one entry: %s',
        node_guid, str(docker_hosts))

  # Process containers concurrently.
  for docker_host in docker_hosts:
    for container in docker.get_containers_with_metrics(gs, docker_host):
      parent_pod_id = utilities.get_parent_pod_id(container)
      if utilities.valid_string(parent_pod_id) and (parent_pod_id in pod_ids):
        # This container is contained in a pod.
        parent_guid = 'Pod:' + parent_pod_id
      else:
        # This container is not contained in a pod.
        parent_guid = node_guid

      # Do not compute the containers by worker threads in test mode
      # because the order of the output will be different than the golden
      # files due to the effects of queuing the work.
      if gs.get_testing():
        _do_compute_container(gs, docker_host, parent_guid, container, g)
      else:
        input_queue.put((
            gs.get_random_priority(),
            _do_compute_container,
            {'gs': gs, 'docker_host': docker_host, 'parent_guid': parent_guid,
             'container': container, 'g': g}))
コード例 #18
0
ファイル: context.py プロジェクト: pkdevbox/cluster-insight
def _do_compute_other_nodes(gs, cluster_guid, nodes_list, oldest_timestamp, g):
  """Adds nodes not in the node list but running pods to the graph.

  This handles the case when there are pods running on the master node,
  in which case we add a dummy node representing the master to the graph.
  The nodes list does not include the master.

  Args:
    gs: the global state.
    cluster_guid: the cluster's ID.
    nodes_list: a list of wrapped Node objects.
    oldest_timestamp: the timestamp of the oldest Node object.
    g: the context graph under construction.
  """
  assert isinstance(gs, global_state.GlobalState)
  assert utilities.valid_string(cluster_guid)
  assert isinstance(nodes_list, list)
  assert utilities.valid_string(oldest_timestamp)
  assert isinstance(g, ContextGraph)

  # Compute the set of known Node names.
  known_node_ids = set()
  for node in nodes_list:
    assert utilities.is_wrapped_object(node, 'Node')
    known_node_ids.add(node['id'])

  # Compute the set of Nodes referenced by pods but not in the known set.
  # The set of unknown node names may be empty.
  missing_node_ids = set()
  for pod in kubernetes.get_pods(gs):
    assert utilities.is_wrapped_object(pod, 'Pod')
    # pod.properties.spec.nodeName may be missing if the pod is waiting.
    parent_node_id = utilities.get_attribute(
        pod, ['properties', 'spec', 'nodeName'])
    if not utilities.valid_string(parent_node_id):
      continue

    if parent_node_id in known_node_ids:
      continue

    # Found a pod that does not belong to any of the known nodes.
    missing_node_ids.add(parent_node_id)

  # Process the missing nodes.
  for node_id in missing_node_ids:
    # Create a dummy node object just as a placeholder for metric
    # annotations.
    node = utilities.wrap_object({}, 'Node', node_id, time.time())

    metrics.annotate_node(node)
    node_guid = 'Node:' + node_id
    g.add_resource(node_guid, node['annotations'], 'Node', oldest_timestamp, {})
    g.add_relation(cluster_guid, node_guid, 'contains')  # Cluster contains Node
コード例 #19
0
def get_rcontrollers(gs):
    """Gets the list of replication controllers in the current cluster.

  Args:
    gs: global state.

  Returns:
    list of wrapped replication controller objects.
    Each element in the list is the result of
    utilities.wrap_object(rcontroller, 'ReplicationController', ...)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    rcontrollers, ts = gs.get_rcontrollers_cache().lookup('')
    if ts is not None:
        app.logger.debug(
            'get_rcontrollers() cache hit returns %d rcontrollers',
            len(rcontrollers))
        return rcontrollers

    rcontrollers = []
    url = get_kubernetes_base_url() + '/replicationcontrollers'

    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and 'items' in result):
        msg = 'invalid result when fetching %s' % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for rcontroller in result['items']:
        name = utilities.get_attribute(rcontroller, ['metadata', 'name'])
        if not utilities.valid_string(name):
            # an invalid replication controller without a valid rcontroller ID.
            continue

        rcontrollers.append(
            utilities.wrap_object(rcontroller, 'ReplicationController', name,
                                  now))

    ret_value = gs.get_rcontrollers_cache().update('', rcontrollers, now)
    app.logger.info('get_rcontrollers() returns %d rcontrollers',
                    len(rcontrollers))
    return ret_value
コード例 #20
0
def get_nodes(gs):
    """Gets the list of all nodes in the current cluster.

  Args:
    gs: global state.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    nodes, timestamp_secs = gs.get_nodes_cache().lookup('')
    if timestamp_secs is not None:
        gs.logger_info('get_nodes() cache hit returns %d nodes', len(nodes))
        return nodes

    nodes = []
    url = '{kubernetes}/nodes'.format(kubernetes=KUBERNETES_API)
    try:
        result = fetch_data(gs, url)
    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()
    if not (isinstance(result, types.DictType) and 'items' in result):
        msg = 'invalid result when fetching %s' % url
        gs.logger_exception(msg)
        raise collector_error.CollectorError(msg)

    for node in result['items']:
        name = utilities.get_attribute(node, ['metadata', 'name'])
        if not utilities.valid_string(name):
            # an invalid node without a valid node ID value.
            continue
        wrapped_node = utilities.wrap_object(
            node,
            'Node',
            name,
            now,
            label=utilities.node_id_to_host_name(name))
        nodes.append(wrapped_node)

    ret_value = gs.get_nodes_cache().update('', nodes, now)
    gs.logger_info('get_nodes() returns %d nodes', len(nodes))
    return ret_value
コード例 #21
0
def get_rcontrollers(gs):
  """Gets the list of replication controllers in the current cluster.

  Args:
    gs: global state.

  Returns:
    list of wrapped replication controller objects.
    Each element in the list is the result of
    utilities.wrap_object(rcontroller, 'ReplicationController', ...)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
  rcontrollers, ts = gs.get_rcontrollers_cache().lookup('')
  if ts is not None:
    gs.logger_info(
        'get_rcontrollers() cache hit returns %d rcontrollers',
        len(rcontrollers))
    return rcontrollers

  rcontrollers = []
  url = get_kubernetes_base_url() + '/replicationcontrollers'

  try:
    result = fetch_data(gs, url)
  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()
  if not (isinstance(result, types.DictType) and 'items' in result):
    msg = 'invalid result when fetching %s' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  for rcontroller in result['items']:
    name = utilities.get_attribute(rcontroller, ['metadata', 'name'])
    if not utilities.valid_string(name):
      # an invalid replication controller without a valid rcontroller ID.
      continue

    rcontrollers.append(utilities.wrap_object(
        rcontroller, 'ReplicationController', name, now))

  ret_value = gs.get_rcontrollers_cache().update('', rcontrollers, now)
  gs.logger_info(
      'get_rcontrollers() returns %d rcontrollers', len(rcontrollers))
  return ret_value
コード例 #22
0
def _get_container_labels(container, parent_pod):
    """Returns key/value pairs identifying all metrics of this container.

  Args:
    container: the container object to annotate.
    parent_pod: the parent pod of 'container'.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
    if not utilities.is_wrapped_object(container, 'Container'):
        return None
    if not utilities.is_wrapped_object(parent_pod, 'Pod'):
        return None

    pod_id = utilities.get_attribute(parent_pod,
                                     ['properties', 'metadata', 'uid'])
    if not utilities.valid_string(pod_id):
        return None

    hostname = utilities.get_attribute(parent_pod,
                                       ['properties', 'spec', 'host'])
    if not utilities.valid_string(hostname):
        return None

    short_container_name = utilities.get_short_container_name(
        container, parent_pod)

    if not utilities.valid_string(short_container_name):
        return None

    return {
        'pod_id': pod_id,
        'hostname': hostname,
        'container_name': short_container_name
    }
コード例 #23
0
def _get_container_labels(container, parent_pod):
  """Returns key/value pairs identifying all metrics of this container.

  Args:
    container: the container object to annotate.
    parent_pod: the parent pod of 'container'.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
  if not utilities.is_wrapped_object(container, 'Container'):
    return None
  if not utilities.is_wrapped_object(parent_pod, 'Pod'):
    return None

  pod_id = utilities.get_attribute(
      parent_pod, ['properties', 'metadata', 'uid'])
  if not utilities.valid_string(pod_id):
    return None

  hostname = utilities.get_attribute(
      parent_pod, ['properties', 'spec', 'host'])
  if not utilities.valid_string(hostname):
    return None

  short_container_name = utilities.get_short_container_name(
      container, parent_pod)

  if not utilities.valid_string(short_container_name):
    return None

  return {
      'pod_id': pod_id,
      'hostname': hostname,
      'container_name': short_container_name
  }
コード例 #24
0
def get_containers_from_pod(pod):
    """Extracts synthesized container resources from a pod.

  Only containers for which status is available are included. (The pod may
  still be pending.)
  """
    assert utilities.is_wrapped_object(pod, 'Pod')
    specs = utilities.get_attribute(pod, ['properties', 'spec', 'containers'])
    statuses = utilities.get_attribute(
        pod, ['properties', 'status', 'containerStatuses'])
    timestamp = pod['timestamp']

    spec_dict = {}
    for spec in specs or []:
        spec = spec.copy()
        spec_dict[spec.pop('name')] = spec

    containers = []
    for status in statuses or []:
        status = status.copy()
        name = status.pop('name')
        unique_id = status.get('containerID', name)
        obj = {
            'metadata': {
                'name': name
            },
            'spec': spec_dict.get(name, {}),
            'status': status,
        }
        container = utilities.wrap_object(obj,
                                          'Container',
                                          unique_id,
                                          timestamp,
                                          label=name)
        containers.append(container)

    return containers
コード例 #25
0
def get_services(gs):
  """Gets the list of services in the current cluster.

  Args:
    gs: global state.

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

    (list_of_services, timestamp_in_seconds)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
  services, timestamp_secs = gs.get_services_cache().lookup('')
  if timestamp_secs is not None:
    gs.logger_info('get_services() cache hit returns %d services',
                   len(services))
    return services

  services = []
  url = '{kubernetes}/services'.format(kubernetes=KUBERNETES_API)
  try:
    result = fetch_data(gs, url)
  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()
  if not (isinstance(result, types.DictType) and 'items' in result):
    msg = 'invalid result when fetching %s' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  for service in result['items']:
    name = utilities.get_attribute(service, ['metadata', 'name'])
    if not utilities.valid_string(name):
      # an invalid service without a valid service ID.
      continue
    services.append(
        utilities.wrap_object(service, 'Service', name, now))

  ret_value = gs.get_services_cache().update('', services, now)
  gs.logger_info('get_services() returns %d services', len(services))
  return ret_value
コード例 #26
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
def _inspect_container(gs, docker_host, container_id):
    """Fetch detailed information about the given container in the given host.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.
    container_id: container ID. Must not be empty.

  Returns:
    (container_information, timestamp_in_seconds) if the container was found.
    (None, None) if the container was not found.

  Raises:
    CollectorError in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
    url = 'http://{docker_host}:{port}/containers/{container_id}/json'.format(
        docker_host=docker_host,
        port=gs.get_docker_port(),
        container_id=container_id)
    # 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 'container_id' is:
    # k8s_php-redis.b317029a_guestbook-controller-ls6k1.default.api_f991d53e-b949-11e4-8246-42010af0c3dd_8dcdfec8
    # Use just the tail of the container ID after the last '_' sign.
    fname = '{host}-container-{id}'.format(host=docker_host.split('.')[0],
                                           id=container_id.split('_')[-1])
    try:
        result = fetch_data(gs, url, fname, expect_missing=True)
    except ValueError:
        # TODO(vasbala): this container does not exist anymore.
        # What should we do here?
        return (None, time.time())
    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)

    # Sort the "Env" attribute because it tends to contain elements in
    # a different order each time you fetch the container information.
    if isinstance(utilities.get_attribute(result, ['Config', 'Env']),
                  types.ListType):
        # Sort the contents of the 'Env' list in place.
        result['Config']['Env'].sort()

    return (result, time.time())
コード例 #27
0
def get_nodes(gs):
  """Gets the list of all nodes in the current cluster.

  Args:
    gs: global state.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
  nodes, timestamp_secs = gs.get_nodes_cache().lookup('')
  if timestamp_secs is not None:
    gs.logger_info('get_nodes() cache hit returns %d nodes', len(nodes))
    return nodes

  nodes = []
  url = get_kubernetes_base_url() + '/nodes'
  try:
    result = fetch_data(gs, url)
  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()
  if not (isinstance(result, types.DictType) and 'items' in result):
    msg = 'invalid result when fetching %s' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  for node in result['items']:
    name = utilities.get_attribute(node, ['metadata', 'name'])
    if not utilities.valid_string(name):
      # an invalid node without a valid node ID value.
      continue
    wrapped_node = utilities.wrap_object(
        node, 'Node', name, now,
        label=utilities.node_id_to_host_name(name))
    nodes.append(wrapped_node)

  ret_value = gs.get_nodes_cache().update('', nodes, now)
  gs.logger_info('get_nodes() returns %d nodes', len(nodes))
  return ret_value
コード例 #28
0
def get_services(gs):
    """Gets the list of services in the current cluster.

  Args:
    gs: global state.

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

    (list_of_services, timestamp_in_seconds)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    services, timestamp_secs = gs.get_services_cache().lookup('')
    if timestamp_secs is not None:
        app.logger.debug('get_services() cache hit returns %d services',
                         len(services))
        return services

    services = []
    url = get_kubernetes_base_url() + '/services'
    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and 'items' in result):
        msg = 'invalid result when fetching %s' % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for service in result['items']:
        name = utilities.get_attribute(service, ['metadata', 'name'])
        if not utilities.valid_string(name):
            # an invalid service without a valid service ID.
            continue
        services.append(utilities.wrap_object(service, 'Service', name, now))

    ret_value = gs.get_services_cache().update('', services, now)
    app.logger.info('get_services() returns %d services', len(services))
    return ret_value
コード例 #29
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
def _inspect_container(gs, docker_host, container_id):
  """Fetch detailed information about the given container in the given host.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.
    container_id: container ID. Must not be empty.

  Returns:
    (container_information, timestamp_in_seconds) if the container was found.
    (None, None) if the container was not found.

  Raises:
    CollectorError in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
  url = 'http://{docker_host}:{port}/containers/{container_id}/json'.format(
      docker_host=docker_host, port=gs.get_docker_port(),
      container_id=container_id)
  # 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 'container_id' is:
  # k8s_php-redis.b317029a_guestbook-controller-ls6k1.default.api_f991d53e-b949-11e4-8246-42010af0c3dd_8dcdfec8
  # Use just the tail of the container ID after the last '_' sign.
  fname = '{host}-container-{id}'.format(
      host=docker_host.split('.')[0], id=container_id.split('_')[-1])
  try:
    result = fetch_data(gs, url, fname, expect_missing=True)
  except ValueError:
    # TODO(vasbala): this container does not exist anymore.
    # What should we do here?
    return (None, time.time())
  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)

  # Sort the "Env" attribute because it tends to contain elements in
  # a different order each time you fetch the container information.
  if isinstance(utilities.get_attribute(result, ['Config', 'Env']),
                types.ListType):
    # Sort the contents of the 'Env' list in place.
    result['Config']['Env'].sort()

  return (result, time.time())
コード例 #30
0
def get_rcontrollers(gs):
    """Gets the list of replication controllers in the current cluster.

  Args:
    gs: global state.

  Returns:
    list of wrapped replication controller objects.
    Each element in the list is the result of
    utilities.wrap_object(rcontroller, 'ReplicationController', ...)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    rcontrollers, ts = gs.get_rcontrollers_cache().lookup("")
    if ts is not None:
        app.logger.debug("get_rcontrollers() cache hit returns %d rcontrollers", len(rcontrollers))
        return rcontrollers

    rcontrollers = []
    url = get_kubernetes_base_url() + "/replicationcontrollers"

    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = "fetching %s failed with exception %s" % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and "items" in result):
        msg = "invalid result when fetching %s" % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for rcontroller in result["items"]:
        name = utilities.get_attribute(rcontroller, ["metadata", "name"])
        if not utilities.valid_string(name):
            # an invalid replication controller without a valid rcontroller ID.
            continue

        rcontrollers.append(utilities.wrap_object(rcontroller, "ReplicationController", name, now))

    ret_value = gs.get_rcontrollers_cache().update("", rcontrollers, now)
    app.logger.info("get_rcontrollers() returns %d rcontrollers", len(rcontrollers))
    return ret_value
コード例 #31
0
def get_services(gs):
    """Gets the list of services in the current cluster.

  Args:
    gs: global state.

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

    (list_of_services, timestamp_in_seconds)

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    services, timestamp_secs = gs.get_services_cache().lookup("")
    if timestamp_secs is not None:
        app.logger.debug("get_services() cache hit returns %d services", len(services))
        return services

    services = []
    url = get_kubernetes_base_url() + "/services"
    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = "fetching %s failed with exception %s" % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and "items" in result):
        msg = "invalid result when fetching %s" % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for service in result["items"]:
        name = utilities.get_attribute(service, ["metadata", "name"])
        if not utilities.valid_string(name):
            # an invalid service without a valid service ID.
            continue
        services.append(utilities.wrap_object(service, "Service", name, now))

    ret_value = gs.get_services_cache().update("", services, now)
    app.logger.info("get_services() returns %d services", len(services))
    return ret_value
コード例 #32
0
ファイル: docker.py プロジェクト: vasbala/cluster-insight
def _inspect_container(gs, docker_host, container_id):
  """Fetch detailed information about the given container in the given host.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.
    container_id: container ID. Must not be empty.

  Returns:
    (container_information, timestamp_in_seconds) if the container was found.
    (None, None) if the container was not found.

  Raises:
    CollectorError in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
  url = 'http://{docker_host}:{port}/containers/{container_id}/json'.format(
      docker_host=docker_host, port=gs.get_docker_port(),
      container_id=container_id)
  fname = utilities.container_id_to_fname(
      docker_host, 'container', container_id)
  try:
    result = fetch_data(gs, url, fname, expect_missing=True)
  except ValueError:
    # TODO(vasbala): this container does not exist anymore.
    # What should we do here?
    return (None, time.time())
  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)

  if not isinstance(result, types.DictType):
    msg = 'fetching %s returns invalid data' % url
    gs.logger_exception(msg)
    raise collector_error.CollectorError(msg)

  # Sort the "Env" attribute because it tends to contain elements in
  # a different order each time you fetch the container information.
  if isinstance(utilities.get_attribute(result, ['Config', 'Env']),
                types.ListType):
    # Sort the contents of the 'Env' list in place.
    result['Config']['Env'].sort()

  return (result, time.time())
コード例 #33
0
def get_pods(gs):
    """Gets the list of all pods in the cluster.

  Args:
    gs: global state.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    pods, timestamp_secs = gs.get_pods_cache().lookup("")
    if timestamp_secs is not None:
        app.logger.debug("get_pods() cache hit returns %d pods", len(pods))
        return pods

    pods = []
    url = get_kubernetes_base_url() + "/pods"
    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = "fetching %s failed with exception %s" % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and "items" in result):
        msg = "invalid result when fetching %s" % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for pod in result["items"]:
        name = utilities.get_attribute(pod, ["metadata", "name"])
        if not utilities.valid_string(name):
            # an invalid pod without a valid pod ID value.
            continue
        wrapped_pod = utilities.wrap_object(pod, "Pod", name, now)
        pods.append(wrapped_pod)

    ret_value = gs.get_pods_cache().update("", pods, now)
    app.logger.info("get_pods() returns %d pods", len(pods))
    return ret_value
コード例 #34
0
def get_pods(gs):
    """Gets the list of all pods in the cluster.

  Args:
    gs: global state.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    pods, timestamp_secs = gs.get_pods_cache().lookup('')
    if timestamp_secs is not None:
        app.logger.debug('get_pods() cache hit returns %d pods', len(pods))
        return pods

    pods = []
    url = get_kubernetes_base_url() + '/pods'
    try:
        result = fetch_data(gs, url)
    except Exception:
        msg = 'fetching %s failed with exception %s' % (url, sys.exc_info()[0])
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    now = time.time()
    if not (isinstance(result, dict) and 'items' in result):
        msg = 'invalid result when fetching %s' % url
        app.logger.exception(msg)
        raise collector_error.CollectorError(msg)

    for pod in result['items']:
        name = utilities.get_attribute(pod, ['metadata', 'name'])
        if not utilities.valid_string(name):
            # an invalid pod without a valid pod ID value.
            continue
        wrapped_pod = utilities.wrap_object(pod, 'Pod', name, now)
        pods.append(wrapped_pod)

    ret_value = gs.get_pods_cache().update('', pods, now)
    app.logger.info('get_pods() returns %d pods', len(pods))
    return ret_value
コード例 #35
0
def _do_compute_container(gs, docker_host, pod_guid, container, g):
  assert isinstance(gs, global_state.GlobalState)
  assert utilities.valid_string(docker_host)
  assert utilities.valid_string(pod_guid)
  assert utilities.is_wrapped_object(container, 'Container')
  assert isinstance(g, ContextGraph)

  container_id = container['id']
  container_guid = 'Container:' + container_id
  # TODO(vasbala): container_id is too verbose?
  g.add_resource(container_guid, container['annotations'],
                 'Container', container['timestamp'],
                 container['properties'])

  # Pod contains Container
  g.add_relation(pod_guid, container_guid, 'contains')

  # Processes in a Container
  for process in docker.get_processes(gs, docker_host, container_id):
    process_id = process['id']
    process_guid = 'Process:' + process_id
    g.add_resource(process_guid, process['annotations'],
                   'Process', process['timestamp'], process['properties'])

    # Container contains Process
    g.add_relation(container_guid, process_guid, 'contains')

  # Image from which this Container was created
  image_id = utilities.get_attribute(
      container, ['properties', 'Config', 'Image'])
  if not utilities.valid_string(image_id):
    # Image ID not found
    return
  image = docker.get_image(gs, docker_host, image_id)
  if image is None:
    # image not found
    return

  image_guid = 'Image:' + image['id']
  # Add the image to the graph only if we have not added it before.
  g.add_resource(image_guid, image['annotations'], 'Image',
                 image['timestamp'], image['properties'])

  # Container createdFrom Image
  g.add_relation(container_guid, image_guid, 'createdFrom')
コード例 #36
0
ファイル: metrics.py プロジェクト: vasbala/cluster-insight
def _get_node_labels(node):
    """Returns key/value pairs identifying all metrics of this node.

  Args:
    node: the node object to annotate.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
    if not utilities.is_wrapped_object(node, "Node"):
        return None

    hostname = utilities.get_attribute(node, ["properties", "metadata", "name"])
    if not utilities.valid_string(hostname):
        return None

    return {"pod_id": "", "hostname": hostname, "container_name": "/"}
コード例 #37
0
def _get_node_labels(node):
    """Returns key/value pairs identifying all metrics of this node.

  Args:
    node: the node object to annotate.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
    if not utilities.is_wrapped_object(node, 'Node'):
        return None

    hostname = utilities.get_attribute(node,
                                       ['properties', 'metadata', 'name'])
    if not utilities.valid_string(hostname):
        return None

    return {'pod_id': '', 'hostname': hostname, 'container_name': '/'}
コード例 #38
0
ファイル: context.py プロジェクト: vasbala/cluster-insight
def _do_compute_pod(gs, input_queue, node_guid, pod, g):
  assert isinstance(gs, global_state.GlobalState)
  assert isinstance(input_queue, Queue.PriorityQueue)
  assert utilities.valid_string(node_guid)
  assert utilities.is_wrapped_object(pod, 'Pod')
  assert isinstance(g, ContextGraph)

  pod_id = pod['id']
  pod_guid = 'Pod:' + pod_id
  docker_host = utilities.get_attribute(
      pod, ['properties', 'spec', 'host'])
  if not utilities.valid_string(docker_host):
    msg = ('Docker host (pod.properties.spec.host) '
           'not found in pod ID %s' % pod_id)
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  g.add_resource(pod_guid, pod['annotations'], 'Pod', pod['timestamp'],
                 pod['properties'])
  g.add_relation(node_guid, pod_guid, 'runs')  # Node runs Pod
コード例 #39
0
def matching_labels(pod, selector):
    """Compares the key/vale pairs in 'selector' with the pod's label.

  The pod is considered to be matching the 'selector' iff
  all of the key/value pairs in 'selector' appear in the pod's "labels"
  value.

  Args:
    pod: the pod to be compared with 'selector'.
    selector: a dictionary of key/value pairs.

  Returns:
    True iff the pod's label matches the key/value pairs in 'selector'.
  """
    pod_labels = utilities.get_attribute(pod, ["properties", "metadata", "labels"])
    if not isinstance(pod_labels, dict):
        return False
    selector_view = selector.viewitems()
    pod_labels_view = pod_labels.viewitems()
    return len(selector_view & pod_labels_view) == len(selector_view)
コード例 #40
0
def matching_labels(pod, selector):
    """Compares the key/vale pairs in 'selector' with the pod's label.

  The pod is considered to be matching the 'selector' iff
  all of the key/value pairs in 'selector' appear in the pod's "labels"
  value.

  Args:
    pod: the pod to be compared with 'selector'.
    selector: a dictionary of key/value pairs.

  Returns:
    True iff the pod's label matches the key/value pairs in 'selector'.
  """
    pod_labels = utilities.get_attribute(pod,
                                         ['properties', 'metadata', 'labels'])
    if not isinstance(pod_labels, types.DictType):
        return False
    selector_view = selector.viewitems()
    pod_labels_view = pod_labels.viewitems()
    return len(selector_view & pod_labels_view) == len(selector_view)
コード例 #41
0
ファイル: docker.py プロジェクト: abhineshwar/cluster-insight
def get_images(gs, docker_host):
  """Gets the list of all images in 'docker_host'.

  Args:
    gs: global state.
    docker_host: Docker host name. Must not be empty.

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

  Raises:
    CollectorError in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
  # The images are already cached by get_image(), so there is no need to
  # check the cache on entry to this method.

  # docker_host is the same as node_id
  images_list = []
  image_id_set = set()

  # All containers in this 'docker_host'.
  for container in get_containers(gs, docker_host):
    # Image from which this Container was created
    image_id = utilities.get_attribute(
        container, ['properties', 'Config', 'Image'])
    if not utilities.valid_string(image_id):
      # Image ID not found
      continue

    image = get_image(gs, docker_host, image_id)
    if (image is not None) and (image['id'] not in image_id_set):
      images_list.append(image)
      image_id_set.add(image['id'])

  gs.logger_info('get_images(docker_host=%s) returns %d images',
                 docker_host, len(images_list))
  return images_list
コード例 #42
0
def _get_node_labels(node):
  """Returns key/value pairs identifying all metrics of this node.

  Args:
    node: the node object to annotate.

  Returns:
  A dictionary of key/value pairs.
  If any error was detected, returns None.
  """
  if not utilities.is_wrapped_object(node, 'Node'):
    return None

  hostname = utilities.get_attribute(node, ['properties', 'metadata', 'name'])
  if not utilities.valid_string(hostname):
    return None

  return {
      'pod_id': '',
      'hostname': hostname,
      'container_name': '/'
  }
コード例 #43
0
def _do_compute_pod(gs, input_queue, node_guid, pod, g):
    assert isinstance(gs, global_state.GlobalState)
    assert isinstance(input_queue, Queue.PriorityQueue)
    assert utilities.valid_string(node_guid)
    assert utilities.is_wrapped_object(pod, 'Pod')
    assert isinstance(g, ContextGraph)

    pod_id = pod['id']
    pod_guid = 'Pod:' + pod_id
    docker_host = utilities.get_attribute(pod, ['properties', 'spec', 'host'])
    if not utilities.valid_string(docker_host):
        msg = ('Docker host (pod.properties.spec.host) '
               'not found in pod ID %s' % pod_id)
        gs.logger_error(msg)
        raise collector_error.CollectorError(msg)

    g.add_resource(pod_guid, pod['annotations'], 'Pod', pod['timestamp'],
                   pod['properties'])
    g.add_relation(node_guid, pod_guid, 'runs')  # Node runs Pod

    # Containers in a Pod
    for container in docker.get_containers_with_metrics(gs, docker_host):
        if not _container_in_pod(gs, container, pod):
            continue

        # Do not compute the containers by worker threads in test mode because the
        # order of the output will be different than the golden files due to the
        # effects of queuing the work.
        if gs.get_testing():
            _do_compute_container(gs, docker_host, pod_guid, container, g)
        else:
            input_queue.put((gs.get_random_priority(), _do_compute_container, {
                'gs': gs,
                'docker_host': docker_host,
                'pod_guid': pod_guid,
                'container': container,
                'g': g
            }))
コード例 #44
0
def _do_compute_pod(gs, input_queue, node_guid, pod, g):
  assert isinstance(gs, global_state.GlobalState)
  assert isinstance(input_queue, Queue.PriorityQueue)
  assert utilities.valid_string(node_guid)
  assert utilities.is_wrapped_object(pod, 'Pod')
  assert isinstance(g, ContextGraph)

  pod_id = pod['id']
  pod_guid = 'Pod:' + pod_id
  docker_host = utilities.get_attribute(
      pod, ['properties', 'spec', 'host'])
  if not utilities.valid_string(docker_host):
    msg = ('Docker host (pod.properties.spec.host) '
           'not found in pod ID %s' % pod_id)
    gs.logger_error(msg)
    raise collector_error.CollectorError(msg)

  g.add_resource(pod_guid, pod['annotations'], 'Pod', pod['timestamp'],
                 pod['properties'])
  g.add_relation(node_guid, pod_guid, 'runs')  # Node runs Pod

  # Containers in a Pod
  for container in docker.get_containers_with_metrics(gs, docker_host):
    if not _container_in_pod(gs, container, pod):
      continue

    # Do not compute the containers by worker threads in test mode because the
    # order of the output will be different than the golden files due to the
    # effects of queuing the work.
    if gs.get_testing():
      _do_compute_container(gs, docker_host, pod_guid, container, g)
    else:
      input_queue.put((
          gs.get_random_priority(),
          _do_compute_container,
          {'gs': gs, 'docker_host': docker_host, 'pod_guid': pod_guid,
           'container': container, 'g': g}))
コード例 #45
0
ファイル: context.py プロジェクト: pkdevbox/cluster-insight
def _do_compute_pod(cluster_guid, pod, g):
  assert utilities.valid_string(cluster_guid)
  assert utilities.is_wrapped_object(pod, 'Pod')
  assert isinstance(g, ContextGraph)

  pod_id = pod['id']
  pod_guid = 'Pod:' + pod_id
  g.add_resource(pod_guid, pod['annotations'], 'Pod', pod['timestamp'],
                 pod['properties'])

  # pod.properties.spec.nodeName may be missing if the pod is waiting
  # (not running yet).
  node_id = utilities.get_attribute(pod, ['properties', 'spec', 'nodeName'])
  if utilities.valid_string(node_id):
    # Pod is running.
    node_guid = 'Node:' + node_id
    g.add_relation(node_guid, pod_guid, 'runs')  # Node runs Pod
  else:
    # Pod is not running.
    g.add_relation(cluster_guid, pod_guid, 'contains')  # Cluster contains Pod

  for container in kubernetes.get_containers_from_pod(pod):
    metrics.annotate_container(container, pod)
    _do_compute_container(pod_guid, container, g)
コード例 #46
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
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
コード例 #47
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
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
コード例 #48
0
def cleanup(result):
  """Removes the attribute OVERSIZE_ATTRIBUTE from 'result'."""
  value = utilities.get_attribute(result, [OVERSIZE_ATTRIBUTE])
  if isinstance(value, types.ListType) and value:
    result[OVERSIZE_ATTRIBUTE] = 'omitted list of %d elements' % len(value)
コード例 #49
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
def get_containers_with_metrics(gs, docker_host):
    """Gets the list of all containers in 'docker_host' with metric annotations.

  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.
  """
    # Create a lookup table from pod IDs to pods.
    # This lookup table is needed when annotating containers with
    # metrics. Also compute the project's name.
    containers_list = get_containers(gs, docker_host)
    if not containers_list:
        return []

    pod_id_to_pod = {}
    project_id = '_unknown_'

    # Populate the pod ID to pod lookup table.
    # Compute the project_id from the name of the first pod.
    for pod in kubernetes.get_pods(gs, docker_host):
        assert utilities.is_wrapped_object(pod, 'Pod')
        pod_id_to_pod[pod['id']] = pod
        if project_id != '_unknown_':
            continue
        pod_hostname = utilities.get_attribute(pod,
                                               ['properties', 'spec', 'host'])
        if utilities.valid_string(pod_hostname):
            project_id = utilities.node_id_to_project_id(pod_hostname)

    # We know that there are containers in this docker_host.
    if not pod_id_to_pod:
        # there are no pods in this docker_host.
        msg = 'Docker host %s has containers but no pods' % docker_host
        gs.logger_exception(msg)
        raise collector_error.CollectorError(msg)

    # Annotate the containers with their metrics.
    for container in containers_list:
        assert utilities.is_wrapped_object(container, 'Container')

        parent_pod_id = utilities.get_parent_pod_id(container)
        if not utilities.valid_string(parent_pod_id):
            msg = ('missing or invalid parent pod ID in container %s' %
                   container['id'])
            gs.logger_error(msg)
            raise collector_error.CollectorError(msg)

        if parent_pod_id not in pod_id_to_pod:
            msg = ('could not locate parent pod %s for container %s' %
                   (parent_pod_id, container['id']))
            gs.logger_error(msg)
            raise collector_error.CollectorError(msg)

        # Note that the project ID may be '_unknown_'.
        # This is not a big deal, because the aggregator knows the project ID.
        metrics.annotate_container(project_id, container,
                                   pod_id_to_pod[parent_pod_id])

    return containers_list
コード例 #50
0
ファイル: docker.py プロジェクト: supriyagarg/cluster-insight
def get_processes(gs, docker_host, container_id):
    """Gets the list of all processes in the 'docker_host' and 'container_id'.

  If the container is not found, returns an empty list of processes.

  Args:
    gs: global state.
    docker_host: the Docker host running the container.
    container_id: the container running the processes.

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

  Raises:
    CollectorError in case of failure to fetch data from Docker.
    Other exceptions may be raised due to exectution errors.
  """
    processes_label = '%s/%s' % (docker_host, container_id)
    processes, timestamp_secs = gs.get_processes_cache().lookup(
        processes_label)
    if timestamp_secs is not None:
        gs.logger_info(
            'get_processes(docker_host=%s, container_id=%s) cache hit',
            docker_host, container_id)
        return processes

    container = get_one_container(gs, docker_host, container_id)
    if container is not None:
        assert utilities.is_wrapped_object(container, 'Container')
        container_short_hex_id = utilities.object_to_hex_id(
            container['properties'])
        assert utilities.valid_string(container_short_hex_id)
    else:
        # Parent container not found. Container might have crashed while we were
        # looking for it.
        return []

    # NOTE: there is no trailing /json in this URL - this looks like a bug in the
    # Docker API
    url = ('http://{docker_host}:{port}/containers/{container_id}/top?'
           'ps_args=aux'.format(docker_host=docker_host,
                                port=gs.get_docker_port(),
                                container_id=container_id))
    # 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 'container_id' is:
    # k8s_php-redis.b317029a_guestbook-controller-ls6k1.default.api_f991d53e-b949-11e4-8246-42010af0c3dd_8dcdfec8
    # Use just the tail of the container ID after the last '_' sign.
    fname = '{host}-processes-{id}'.format(host=docker_host.split('.')[0],
                                           id=container_id.split('_')[-1])

    try:
        # TODO(vasbala): what should we do in cases where the container is gone
        # (and replaced by a different one)?
        result = fetch_data(gs, url, fname, expect_missing=True)
    except ValueError:
        # this container does not exist anymore
        return []
    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)

    if not isinstance(utilities.get_attribute(result, ['Titles']),
                      types.ListType):
        invalid_processes(gs, url)
    if not isinstance(utilities.get_attribute(result, ['Processes']),
                      types.ListType):
        invalid_processes(gs, url)

    pstats = result['Titles']
    processes = []
    now = time.time()
    for pvalues in result['Processes']:
        process = {}
        if not isinstance(pvalues, types.ListType):
            invalid_processes(gs, url)
        if len(pstats) != len(pvalues):
            invalid_processes(gs, url)
        for pstat, pvalue in zip(pstats, pvalues):
            process[pstat] = pvalue

        # Prefix with container Id to ensure uniqueness across the whole graph.
        process_id = '%s/%s' % (container_short_hex_id, process['PID'])
        processes.append(
            utilities.wrap_object(process,
                                  'Process',
                                  process_id,
                                  now,
                                  label=process['PID']))

    ret_value = gs.get_processes_cache().update(processes_label, processes,
                                                now)
    gs.logger_info(
        'get_processes(docker_host=%s, container_id=%s) returns %d processes',
        docker_host, container_id, len(processes))
    return ret_value
コード例 #51
0
def get_pods(gs, node_id=None):
    """Gets the list of all pods in the given node or in the cluster.

  When 'node_id' is None, it returns the list of pods in the cluster.
  When 'node_id' is a non-empty string, it returns the list of pods in that
  node.

  Args:
    gs: global state.
    node_id: the parent node of the pods or None.

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

  Raises:
    CollectorError: in case of failure to fetch data from Kubernetes.
    Other exceptions may be raised due to exectution errors.
  """
    pods_label = '' if node_id is None else node_id
    pods, timestamp_secs = gs.get_pods_cache().lookup(pods_label)
    if timestamp_secs is not None:
        gs.logger_info('get_pods(pods_label=%s) cache hit returns %d pods',
                       pods_label, len(pods))
        return pods

    pods = []
    url = '{kubernetes}/pods'.format(kubernetes=KUBERNETES_API)
    try:
        result = fetch_data(gs, url)
    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()
    if not (isinstance(result, types.DictType) and 'items' in result):
        msg = 'invalid result when fetching %s' % url
        gs.logger_exception(msg)
        raise collector_error.CollectorError(msg)

    for pod in result['items']:
        name = utilities.get_attribute(pod, ['metadata', 'name'])
        if not utilities.valid_string(name):
            # an invalid pod without a valid pod ID value.
            continue
        wrapped_pod = utilities.wrap_object(pod, 'Pod', name, now)
        if node_id:
            # pod['spec']['host'] may be missing if the pod is in "Waiting"
            # status.
            if utilities.get_attribute(pod, ['spec', 'host']) == node_id:
                pods.append(wrapped_pod)
        else:
            # append pod to output if 'node_id' is not specified.
            pods.append(wrapped_pod)

    ret_value = gs.get_pods_cache().update(pods_label, pods, now)
    gs.logger_info('get_pods(node_id=%s) returns %d pods', pods_label,
                   len(pods))
    return ret_value