コード例 #1
0
 def __init__(self, value: str, container: ContainerInterface,
              common_labels: dict):
     assert isinstance(value, str)
     self.cgroup = container.get_cgroup()
     self._original_value = value
     self.value = _parse_cpuset(value)
     self.common_labels = common_labels
     self.labels_updater = LabelsUpdater(common_labels or {})
     # First core
     self.min_value = 0
     # Last core
     self.max_value = self.cgroup.platform_cpus - 1
コード例 #2
0
 def __init__(self, value: str, container: ContainerInterface, common_labels: dict):
     assert isinstance(value, str)
     self.cgroup = container.get_cgroup()
     self.subcgroups = container.get_subcgroups()
     self.value = value
     self.common_labels = common_labels
     self.labels_updater = LabelsUpdater(common_labels or {})
     self.min_value = 0
     self.max_value = None  # TO BE UPDATED by subclass
コード例 #3
0
ファイル: test_allocations_dict.py プロジェクト: Damenus/owca
def test_labels_updater(input_metrics, common_labels, expected_metrics):
    LabelsUpdater(common_labels).update_labels(input_metrics)
    assert input_metrics == expected_metrics
コード例 #4
0
 def __post_init__(self):
     assert isinstance(self.rdt_allocation,
                       RDTAllocation), 'type error on %r' % self
     self.labels_updater = LabelsUpdater(self.common_labels)
コード例 #5
0
class RDTAllocationValue(AllocationValue):
    """Wrapper over immutable RDTAllocation object to perform validation, serialization
    and enforce isolation on RDT resources."""

    # Name of tasks, that RDTAllocation was assigned to.
    # Is used as resgroup.name if RDTAllocation.name is None
    container_name: str
    rdt_allocation: RDTAllocation
    resgroup: ResGroup
    get_pids: Callable[[], List[str]]  # Used as pid provider
    platform_sockets: int
    rdt_information: RDTInformation
    rdt_groups: RDTGroups
    common_labels: Dict[str, str]
    source_resgroup: Optional[
        ResGroup] = None  # if not none try to _cleanup it at the end

    def __post_init__(self):
        assert isinstance(self.rdt_allocation,
                          RDTAllocation), 'type error on %r' % self
        self.labels_updater = LabelsUpdater(self.common_labels)

    def __repr__(self):
        return repr(self.rdt_allocation)

    def __eq__(self, other):
        return self.rdt_allocation == other.rdt_allocation

    def _copy(self,
              rdt_allocation: RDTAllocation,
              source_resgroup=None,
              resgroup=None):
        return RDTAllocationValue(
            container_name=self.container_name,
            rdt_allocation=rdt_allocation,
            get_pids=self.get_pids,
            resgroup=resgroup if resgroup is not None else self.resgroup,
            platform_sockets=self.platform_sockets,
            rdt_information=self.rdt_information,
            source_resgroup=source_resgroup,
            rdt_groups=self.rdt_groups,
            common_labels=self.common_labels)

    def generate_metrics(self) -> List[Metric]:
        """Encode RDT Allocation as metrics.
        Note:
        - cache allocation: generated two metrics, with number of cache ways and
                            mask of bits (encoded as int)
        - memory bandwidth: is encoded as int, representing MB/s or percentage
        """
        # Empty object generate no metric.
        if not self.rdt_allocation.l3 and not self.rdt_allocation.mb:
            return []

        group_name = self.get_resgroup_name()

        metrics = []
        if self.rdt_allocation.l3:
            domains = _parse_schemata_file_row(self.rdt_allocation.l3)
            for domain_id, raw_value in domains.items():
                metrics.extend([
                    Metric(name='allocation_rdt_l3_cache_ways',
                           value=_count_enabled_bits(raw_value),
                           type=MetricType.GAUGE,
                           labels=dict(
                               allocation_type='rdt_l3_cache_ways',
                               group_name=group_name,
                               domain_id=domain_id,
                               container_name=self.container_name,
                           )),
                    Metric(name='allocation_rdt_l3_mask',
                           value=int(raw_value, 16),
                           type=MetricType.GAUGE,
                           labels=dict(
                               allocation_type='rdt_l3_mask',
                               group_name=group_name,
                               domain_id=domain_id,
                               container_name=self.container_name,
                           ))
                ])

        if self.rdt_allocation.mb:
            domains = _parse_schemata_file_row(self.rdt_allocation.mb)
            for domain_id, raw_value in domains.items():
                # NOTE: raw_value is treated as int, ignoring unit used (MB or %)
                value = int(raw_value)
                metrics.append(
                    Metric(name='allocation_rdt_mb',
                           value=value,
                           type=MetricType.GAUGE,
                           labels=dict(
                               allocation_type='rdt_mb',
                               group_name=group_name,
                               domain_id=domain_id,
                               container_name=self.container_name,
                           )))

        self.labels_updater.update_labels(metrics)

        return metrics

    def get_resgroup_name(self):
        """Return explicitly set resgroup name of inferred from covering container. """
        return self.rdt_allocation.name if self.rdt_allocation.name is not None \
            else self.container_name

    def calculate_changeset(self, current: Optional['RDTAllocationValue']):
        """Merge with existing RDTAllocation objects and return
        sum of the allocations (target_rdt_allocation)
        and allocations that need to be updated (rdt_allocation_changeset).

        current can be None - means we have just spotted the task, and we're moving
                it from default root group.

        current cannot have empty name in rdt_allocation.name !!!!
        """
        assert isinstance(
            current,
            (type(None),
             RDTAllocationValue)), 'type error on current=%r ' % current
        # Any rdt_allocation that comes with current have to have rdt_allocation.name set)
        assert current is None or (current.rdt_allocation is not None)

        new: RDTAllocationValue = self
        new_group_name = new.get_resgroup_name()

        # new name, then new allocation will be used (overwrite) but no merge
        if current is None:
            # New tasks or is moved from root group.
            log.debug(
                'resctrl changeset: new name or no previous allocation exists (moving from root '
                'group!)')
            return new, new._copy(new.rdt_allocation,
                                  resgroup=ResGroup(name=new_group_name),
                                  source_resgroup=ResGroup(name=''))

        current_group_name = current.get_resgroup_name()

        if current_group_name != new_group_name:
            # We need to move to another group.
            log.debug('resctrl changeset: move to new group=%r from=%r',
                      current_group_name, new_group_name)
            return new, new._copy(
                new.rdt_allocation,
                resgroup=ResGroup(name=new_group_name),
                source_resgroup=ResGroup(name=current.get_resgroup_name()))
        else:
            log.debug(
                'resctrl changeset: merging existing rdt allocation (the same resgroup name)'
            )

            # Prepare target first, overwrite current l3 & mb values with new
            target_rdt_allocation = RDTAllocation(
                name=current.rdt_allocation.name,
                l3=new.rdt_allocation.l3 or current.rdt_allocation.l3,
                mb=new.rdt_allocation.mb or current.rdt_allocation.mb,
            )
            target = current._copy(target_rdt_allocation)

            # Prepare changeset
            # Logic: if new value exists and is different from old one use the new.
            if _is_rdt_suballocation_changed(current.rdt_allocation.l3,
                                             new.rdt_allocation.l3):
                new_l3 = new.rdt_allocation.l3
            else:
                log.debug('changeset l3: no change between: %r and %r' %
                          (current.rdt_allocation.l3, new.rdt_allocation.l3))
                new_l3 = None

            if _is_rdt_suballocation_changed(current.rdt_allocation.mb,
                                             new.rdt_allocation.mb):
                new_mb = new.rdt_allocation.mb
            else:
                log.debug('changeset l3: no change between: %r and %r' %
                          (current.rdt_allocation.mb, new.rdt_allocation.mb))
                new_mb = None

            if new_l3 or new_mb:
                # Only return something if schemata resources differs.
                rdt_allocation_changeset = RDTAllocation(
                    name=new.rdt_allocation.name,
                    l3=new_l3,
                    mb=new_mb,
                )
                changeset = current._copy(rdt_allocation_changeset)
                return target, changeset
            else:
                return target, None

    def validate(self):
        """Check L3 mask according platform.rdt_ features."""
        if self.rdt_allocation.l3:
            if not self.rdt_information.rdt_cache_control_enabled:
                raise InvalidAllocations(
                    'Allocator requested RDT cache allocation but '
                    'RDT cache control is not enabled!')

            validate_l3_string(self.rdt_allocation.l3, self.platform_sockets,
                               self.rdt_information.cbm_mask,
                               self.rdt_information.min_cbm_bits)
        if self.rdt_allocation.mb:
            if not self.rdt_information.rdt_mb_control_enabled:
                raise InvalidAllocations(
                    'Allocator requested RDT MB allocation but '
                    'RDT memory bandwidth is not enabled!')
            normalized_mb_string = normalize_mb_string(
                self.rdt_allocation.mb, self.platform_sockets,
                self.rdt_information.mb_min_bandwidth,
                self.rdt_information.mb_bandwidth_gran)

            replace(self.rdt_allocation, mb=normalized_mb_string)

        self.rdt_groups.validate(self)

    def perform_allocations(self):
        """Enforce L3 or MB isolation including:
        - moving to new group if source_group is not None
        - update schemata file for given RDT resources
        - remove old group (source) optional
        """

        # Move to appropriate group first.
        if self.source_resgroup is not None:
            log.debug(
                'resctrl: perform_allocations moving to new group (from %r to %r)',
                self.source_resgroup.name, self.resgroup.name)

            # three cases (to root, from root, or between new resgroups)
            self.resgroup.add_pids(pids=self.get_pids(),
                                   mongroup_name=self.container_name)

            if len(self.source_resgroup.get_mon_groups()) == 1:
                self.source_resgroup.remove(self.container_name)

        # Now update the schemata file.
        if self.rdt_groups.should_perform_schemata_write(self):
            lines = []
            if self.rdt_allocation.l3 and \
                    self.rdt_information.rdt_cache_control_enabled:
                lines.append(self.rdt_allocation.l3)
            if self.rdt_allocation.mb and \
                    self.rdt_information.rdt_mb_control_enabled:
                lines.append(self.rdt_allocation.mb)
            if lines:
                log.debug('resctrl: perform_allocations update schemata in %r',
                          self.resgroup.name)
                self.resgroup.write_schemata(lines)
コード例 #6
0
class CPUSetAllocationValue(AllocationValue):
    def __init__(self, value: str, container: ContainerInterface,
                 common_labels: dict):
        assert isinstance(value, str)
        self.cgroup = container.get_cgroup()
        self._original_value = value
        self.value = _parse_cpuset(value)
        self.common_labels = common_labels
        self.labels_updater = LabelsUpdater(common_labels or {})
        # First core
        self.min_value = 0
        # Last core
        self.max_value = self.cgroup.platform_cpus - 1

    def __repr__(self):
        return repr(self.value)

    def __eq__(self, other: 'CPUSetAllocationValue') -> bool:
        """Compare cpuset value to another value by taking value into consideration."""
        assert isinstance(other, CPUSetAllocationValue)
        return self.value == other.value

    def calculate_changeset(self, current: 'CPUSetAllocationValue') \
            -> Tuple['CPUSetAllocationValue', Optional['CPUSetAllocationValue']]:
        if current is None:
            # There is no old value, so there is a change
            value_changed = True
        else:
            # If we have old value compare them.
            assert isinstance(current, CPUSetAllocationValue)
            value_changed = (self != current)

        if value_changed:
            return self, self
        else:
            return current, None

    def generate_metrics(self) -> List[Metric]:
        assert isinstance(self.value, list)
        metrics = [
            Metric(name='allocation_cpuset',
                   value=self.value,
                   type=MetricType.GAUGE,
                   labels=dict(allocation_type='cpuset'))
        ]
        self.labels_updater.update_labels(metrics)
        return metrics

    def validate(self):
        if len(self.value) > 0:
            if self.value[0] < self.min_value or self.value[
                    -1] > self.max_value:
                raise InvalidAllocations('{} not in range <{};{}>'.format(
                    self._original_value, self.min_value, self.max_value))
        else:
            raise InvalidAllocations('{} is invalid argument!'.format(
                self._original_value))

    def perform_allocations(self):
        self.validate()
        normalized_cpus = _normalize_cpuset(self.value)
        normalized_mems = _normalize_cpuset(
            list(range(0, self.cgroup.platform_sockets)))
        self.cgroup.set_cpuset(normalized_cpus, normalized_mems)