def am_i_involved(self, apiobj): # Does the object has any ownerReferences? ref = apiobj.model.involvedObject if ref is Missing: return False ''' Example: kind: Event ... involvedObject: apiVersion: apps.openshift.io/v1 kind: DeploymentConfig name: crel-monitors-app-creation-test namespace: openshift-monitoring resourceVersion: "1196701489" uid: 675a1b29-d862-11e8-8383-02d8407159d1 ''' if kind_matches(self.kind(), ref.kind) and self.name( ) == ref.name and self.namespace() == ref.namespace: return True return False
def is_kind(self, test_kind_or_kind_list): """ apiobj.is_kind('pod') or apiobj.is_kind(['pod', 'ds']) :param test_kind_or_kind_list: A str or list of strings to match :return: Returns whether this apiobj represents the specified kind or list of kings. """ return kind_matches(self.kind(), test_kind_or_kind_list)
def related(self, find_kind): """ Returns a dynamic selector which all of a the specified kind of object which is related to this object. For example: - if this object is a node, and find_kind=='pod', it will find all pods associated with the node. - if this object is a template and find_kind=='buildconfig', it will select buildconfigs created by this template. - if this object is a buildconfig and find_kind='builds', builds created by this buildconfig will be selected. :return: A selector which selects objects of kind find_kind which are related to this object. """ labels = {} this_kind = self.kind() name = self.name() # TODO: add rc, rs, ds, project, ... ? if kind_matches(this_kind, 'node') and kind_matches(find_kind, 'pod'): return selector('pod', all_namespaces=True, field_selectors={'spec.nodeName': self.name()}) if this_kind.startswith("template"): labels["template"] = name elif this_kind.startswith("deploymentconfig"): labels["deploymentconfig"] = name elif this_kind.startswith("deployment"): labels["deployment"] = name elif this_kind.startswith("buildconfig"): labels["openshift.io/build-config.name"] = name elif this_kind.startswith("statefulset"): labels["statefulset.kubernetes.io/pod-name"] = name elif this_kind.startswith("job"): labels["job-name"] = name else: raise OpenShiftPythonException( "Unknown how to find {} resources to related to kind: {}". format(find_kind, this_kind)) return selector(find_kind, labels=labels, static_context=self.context)
def do_i_own(self, apiobj): # Does the object has any ownerReferences? if apiobj.model.metadata.ownerReferences is Missing: return False ''' Example: ownerReferences: - apiVersion: v1 blockOwnerDeletion: true controller: true kind: ReplicationController name: ruby-hello-world-1 uid: 50347024-a615-11e8-8841-0a46c474dfe0 ''' for ref in apiobj.model.metadata.ownerReferences: if kind_matches(self.kind(), ref.kind) and self.name() == ref.name: return True return False
def get_events(self): """ Returns a list of apiobjects events which indicate this object as their involvedObject. This can be an expensive if there are a large number of events to search. :return: A (potentially empty) list of event APIObjects """ # If this is a project, just return all events in the namespace. if kind_matches(self.kind(), ['project', 'namespace']): return selector('events').objects() involved = [] def check_if_involved(apiobj): if self.am_i_involved(apiobj): involved.append(apiobj) selector('events', static_context=self.context).for_each(check_if_involved) return involved
def logs(self, timestamps=False, previous=False, since=None, limit_bytes=None, tail=-1, cmd_args=None, try_longshots=True): """ Attempts to collect logs from running pods associated with this resource. Supports daemonset, statefulset, deploymentconfig, deployment, replicationcontroller, replicationset, buildconfig, build, pod. If a resource is associated with many pods, all pods owned by that resource will be individually scraped for logs. For example, if a daemonset is specified, an invocation of `oc logs ...` will be made for each pod associated with that daemonset -- this is different from the output of `oc logs ds/name`. If try_longshots==True, logs can also be collected to for any object which directly owns pods or responds successfully with "oc logs kind/name". Since pods can be pending or otherwise unable to deliver logs, if an error is encountered during an 'oc logs' invocation, the stderr will be considered the 'logs' of the object. In other words, oc returning an error will not terminate this function. :param cmd_args: An optional list of additional arguments to pass on the command line :param try_longshots: If True, an attempt we will be made to collect logs from resources which the library does not natively understand to possess logs. If False and the object is not recognized, an empty dict will be returned. :return: Returns a dict of {<fully-qualified-name> -> <log output>}. The fully qualified name will be a human readable, unique identifier containing namespace, object, and container-name (when applicable). """ log_aggregation = {} def add_entry(collection, entry_key, action): entry = action.out if action.status != 0: entry += u'\n>>>>Error during log collection rc={}<<<<\n{}\n'.format( action.status, action.err) entry = entry.strip().replace('\r\n', '\n') collection[entry_key] = entry base_args = list() if previous: base_args.append('-p') if since: base_args.append('--since={}'.format(since)) if limit_bytes: base_args.append('--limit-bytes={}'.format(limit_bytes)) if timestamps: base_args.append('--timestamps') base_args.append('--tail={}'.format(tail)) pod_list = [] if kind_matches(self.kind(), 'pod'): pod_list.append(self) elif kind_matches(self.kind(), ['ds', 'statefulset']): pod_list.extend(self.get_owned('pod')) elif kind_matches(self.kind(), 'deployment'): for rs in self.get_owned('rs'): pod_list.extend(rs.get_owned('pod')) elif kind_matches(self.kind(), 'dc'): for rc in self.get_owned('rc'): pod_list.extend(rc.get_owned('pod')) elif kind_matches(self.kind(), ['rs', 'rc']): pod_list.extend(self.get_owned('pod')) elif kind_matches(self.kind(), ['bc', 'build']): action = oc_action(self.context, "logs", cmd_args=[base_args, cmd_args, self.qname()], namespace=self.namespace(if_missing=None)) add_entry(log_aggregation, self.fqname(), action) else: if try_longshots: # If the kind directly owns pods, we can find the logs for it pod_list.extend(self.get_owned('pod')) if not pod_list: # Just try to collect logs and see what happens action = oc_action( self.context, "logs", cmd_args=[base_args, cmd_args, self.qname()], namespace=self.namespace(if_missing=None)) add_entry(log_aggregation, self.fqname(), action) else: # We don't recognize kind and we aren't taking longshots. return dict() for pod in pod_list: for container in pod.model.spec.containers: action = oc_action( self.context, "logs", cmd_args=[ base_args, cmd_args, pod.qname(), '-c', container.name, '--namespace={}'.format(pod.namespace()) ], no_namespace= True # Namespace is included in cmd_args, do not use context ) # Include self.fqname() to let reader know how we actually found this pod (e.g. from a dc). key = '{}->{}({})'.format(self.fqname(), pod.qname(), container.name) add_entry(log_aggregation, key, action) return log_aggregation