1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
|
# Copyright 2015 IBM Corp.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Extended validation utilities."""
import abc
import six
from oslo_log import log as logging
from pypowervm.i18n import _
from pypowervm.wrappers import base_partition as bp
LOG = logging.getLogger(__name__)
class ValidatorException(Exception):
"""Exceptions thrown from the validators."""
pass
class LPARWrapperValidator(object):
"""LPAR Validator.
This class implements additional validation for LPARs for use
during resize or deployment.
It is meant to catch any violations that would cause errors at
the PowerVM management interface.
"""
def __init__(self, lpar_w, host_w, cur_lpar_w=None):
"""Initialize the validator
:param lpar_w: LPAR wrapper intended to be validated
:param host_w: managed_system wrapper intended to be validated
against such as making sure the host has the desired resources
of the LPAR available.
:param cur_lpar_w: (Optional) current LPAR wrapper used to validate
deltas during a resize operation. If this is passed in then it
assumes resize validation.
"""
self.lpar_w = lpar_w
self.host_w = host_w
self.cur_lpar_w = cur_lpar_w
def validate_all(self, check_dlpar=True):
"""Invoke attribute validation classes to perform validation
:param check_dlpar: indicates the need to validate the dlpar
capability. It is False when update is requested with force
option.
"""
ProcValidator(self.lpar_w, self.host_w,
cur_lpar_w=self.cur_lpar_w).validate(
check_dlpar=check_dlpar)
MemValidator(self.lpar_w, self.host_w,
cur_lpar_w=self.cur_lpar_w).validate(
check_dlpar=check_dlpar)
CapabilitiesValidator(self.lpar_w, self.host_w,
cur_lpar_w=self.cur_lpar_w).validate(
check_dlpar=check_dlpar)
@six.add_metaclass(abc.ABCMeta)
class BaseValidator(object):
"""Base Validator.
This class is responsible for delegating validation depending on
if it's a deploy, active resize, or inactive resize.
If the caller intends to perform resize validation then they must pass
the cur_lpar_w argument. The cur_lpar_w is the current LPAR wrapper
describing the LPAR before any resizing has taken place, while lpar_w
represents the LPAR with new (resized) values. If cur_lpar_w is None
then deploy validation logic will ensue.
"""
def __init__(self, lpar_w, host_w, cur_lpar_w=None):
"""Initialize LPAR and System Wrappers."""
self.lpar_w = lpar_w
self.host_w = host_w
self.cur_lpar_w = cur_lpar_w
def validate(self, check_dlpar=True):
"""Determines what validation is requested and invokes it.
:param check_dlpar: flag indicating if we need to validate dlpar
capability.
"""
# Deploy
self._populate_new_values()
if self.cur_lpar_w is None:
self._validate_deploy()
# Resize
else:
if check_dlpar:
self._can_modify()
self._populate_resize_diffs()
# Inactive Resize
if self.cur_lpar_w.state == bp.LPARState.NOT_ACTIVATED:
self._validate_inactive_resize()
# Active Resize
else:
self._validate_active_resize()
self._validate_common()
@abc.abstractmethod
def _populate_new_values(self):
"""Abstract method for populating deploy values
This method will always be called in validate() and should
populate instance attributes with the new LPARWrapper
values.
"""
@abc.abstractmethod
def _populate_resize_diffs(self):
"""Abstract method for populating resize values
This method will only be called in validate() for resize
operations and should populate instance attributes with
the differences between the old and new LPARWrapper values.
"""
@abc.abstractmethod
def _validate_deploy(self):
"""Abstract method for deploy validation only."""
@abc.abstractmethod
def _validate_active_resize(self):
"""Abstract method for active resize validation only."""
@abc.abstractmethod
def _validate_inactive_resize(self):
"""Abstract method for inactive resize validation only."""
@abc.abstractmethod
def _validate_common(self):
"""Abstract method for common validation
This method should be agnostic to the operation being validated
(deploy or resize) because the instance attributes will
be populated accordingly in validate().
"""
@abc.abstractmethod
def _can_modify(self):
"""Abstract method to check if resource may be modified
This method should invoke the corresponding can_modify
method in the LPAR class for the resource and raise an
exception if it returns False. Should only be called for
resize validation when cur_lpar_w is passed in.
"""
def _validate_host_has_available_res(self, des, avail, res_name):
if round(des, 2) > round(avail, 2):
ex_args = {'requested': '%.2f' % des,
'avail': '%.2f' % avail,
'instance_name': self.lpar_w.name,
'res_name': res_name}
msg = _("Insufficient available %(res_name)s on host for virtual "
"machine '%(instance_name)s' (%(requested)s "
"requested, %(avail)s available)") % ex_args
LOG.error(msg)
raise ValidatorException(msg)
class MemValidator(BaseValidator):
"""Memory Validator.
This class implements memory validation for lpars in the case of
deploy, inactive resize, and active resize.
Instance attributes populated by _populate_new_values
:attr des_mem: desired memory of the new lpar
:attr max_mem: maximum memory of the new lpar
:attr min_mem: minimum memory of the new lpar
:attr avail_mem: available memory on the host
:attr ppt_ratio: desired ppt ratio of the new lpar
:attr res_name: name of the resource
"""
def __init__(self, lpar_w, host_w, cur_lpar_w=None):
super(MemValidator, self).__init__(lpar_w, host_w,
cur_lpar_w=cur_lpar_w)
self.ppt_ratio = None
def _populate_new_values(self):
"""Set newly desired LPAR attributes as instance attributes."""
mem_cfg = self.lpar_w.mem_config
self.des_mem = mem_cfg.desired
self.max_mem = mem_cfg.max
self.min_mem = mem_cfg.min
self.exp_fact = mem_cfg.exp_factor
self.avail_mem = self.host_w.memory_free
self.ppt_ratio = mem_cfg.ppt_ratio
self.res_name = _('memory')
def _populate_resize_diffs(self):
"""Calculate lpar_w vs cur_lpar_w diffs and set as attributes."""
deltas = self._calculate_resize_deltas()
self.delta_des_mem = deltas['delta_mem']
self.delta_max_mem = deltas['delta_max_mem']
self.delta_exp_fact = deltas['delta_exp_factor']
def _validate_deploy(self):
"""Enforce validation rules specific to LPAR deployment."""
self._validate_host_has_available_res(
self.des_mem, self.avail_mem, self.res_name)
def _validate_active_resize(self):
"""Enforce validation rules specific to active resize."""
curr_mem_cfg = self.cur_lpar_w.mem_config
curr_min_mem = curr_mem_cfg.min
curr_max_mem = curr_mem_cfg.max
# min/max values cannot be changed when lpar is not powered off.
if self.max_mem != curr_max_mem or self.min_mem != curr_min_mem:
msg = (_("The virtual machine must be powered off before changing "
"the minimum or maximum memory. Power off virtual "
"machine %s and try again.") % self.cur_lpar_w.name)
raise ValidatorException(msg)
if self.delta_exp_fact != 0:
msg = (_("The virtual machine must be powered off before changing "
"the expansion factor. Power off virtual machine %s and "
"try again.") % self.cur_lpar_w.name)
raise ValidatorException(msg)
if (self.ppt_ratio is not None and
self.ppt_ratio != curr_mem_cfg.ppt_ratio):
msg = ("The virtual machine must be powered off before changing "
"the physical page table ratio. Power off virtual "
"machine %s and try again.") % self.cur_lpar_w.name
raise ValidatorException(msg)
# Common validations for both active & inactive resizes.
self._validate_resize_common()
def _validate_inactive_resize(self):
"""Enforce validation rules specific to inactive resize."""
self._validate_resize_common()
def _validate_common(self):
"""Enforce operation agnostic validation rules."""
# TODO(IBM):
pass
def _can_modify(self):
"""Checks mem dlpar and rmc state if LPAR not activated."""
modifiable, reason = self.cur_lpar_w.can_modify_mem()
if not modifiable:
LOG.error(reason)
raise ValidatorException(reason)
def _validate_resize_common(self):
"""Validation rules common for both active and inactive resizes.
Helper method to enforce validation rules that are common for
both active and inactive resizes.
"""
self._validate_host_has_available_res(self.delta_des_mem,
self.avail_mem,
self.res_name)
def _calculate_resize_deltas(self):
"""Helper method to calculate the memory deltas for resize operation.
:return dict of memory deltas.
"""
deltas = {}
# Current LPAR values
curr_mem_cfg = self.cur_lpar_w.mem_config
curr_des_mem = curr_mem_cfg.desired
curr_max_mem = curr_mem_cfg.max
curr_exp_fact = curr_mem_cfg.exp_factor
# Calculate memory deltas
deltas['delta_mem'] = self.des_mem - curr_des_mem
deltas['delta_max_mem'] = self.max_mem - curr_max_mem
deltas['delta_exp_factor'] = self.exp_fact - curr_exp_fact
return deltas
class ProcValidator(BaseValidator):
"""Processor Validator.
This class implements processor validation for LPARs in the case of
deploy, inactive resize, and active resize.
Instance attributes populated by _populate_new_values
:attr has_dedicated: LPAR has dedicated processors boolean
:attr procs_avail: available procs on host
:attr des_procs: desired processors from new LPAR
:attr res_name: name of the resource
:attr max_procs_per_aix_linux_lpar: max procs per LPAR on host
:attr max_sys_procs_limit: LPAR max procs limit on host
:attr des_vcpus: LPAR desired vcpus
:attr max_vcpus: LPAR max vcpus
:attr min_vcpus: LPAR min vcpus
:attr proc_compat_mode: Processor compatibility mode
:attr pool_id: LPAR shared processor pool ID (only for shared proc mode)
:attr max_proc_units: LPAR max proc units (only for shared processor mode)
:attr min_proc_units: LPAR min proc units (only for shared processor mode)
"""
def _populate_new_values(self):
"""Set newly desired LPAR values as instance attributes."""
self.has_dedicated = self.lpar_w.proc_config.has_dedicated
self.procs_avail = self.host_w.proc_units_avail
self.proc_compat_mode = self.lpar_w.proc_compat_mode
if self.has_dedicated:
self._populate_dedicated_proc_values()
else:
self._populate_shared_proc_values()
def _populate_dedicated_proc_values(self):
"""Set dedicated proc values as instance attributes."""
ded_proc_cfg = self.lpar_w.proc_config.dedicated_proc_cfg
self.des_procs = ded_proc_cfg.desired
self.res_name = _('CPUs')
# Proc host limits for dedicated proc
self.max_procs_per_aix_linux_lpar = (
self.host_w.max_procs_per_aix_linux_lpar)
self.max_sys_procs_limit = self.host_w.max_sys_procs_limit
# VCPUs doesn't mean anything in dedicated proc cfg
# FAIP in dedicated proc cfg vcpus == procs for naming convention
self.des_vcpus = self.des_procs
self.max_vcpus = ded_proc_cfg.max
self.min_vcpus = ded_proc_cfg.min
def _populate_shared_proc_values(self):
"""Set shared proc values as instance attributes."""
shr_proc_cfg = self.lpar_w.proc_config.shared_proc_cfg
self.des_procs = shr_proc_cfg.desired_units
self.res_name = _('processing units')
# VCPU host limits for shared proc
self.max_procs_per_aix_linux_lpar = (
self.host_w.max_vcpus_per_aix_linux_lpar)
self.max_sys_procs_limit = self.host_w.max_sys_vcpus_limit
self.des_vcpus = shr_proc_cfg.desired_virtual
self.max_vcpus = shr_proc_cfg.max_virtual
self.min_vcpus = shr_proc_cfg.min_virtual
self.max_proc_units = shr_proc_cfg.max_units
self.min_proc_units = shr_proc_cfg.min_units
self.pool_id = shr_proc_cfg.pool_id
def _populate_resize_diffs(self):
"""Calculate lpar_w vs cur_lpar_w diffs and set as attributes."""
deltas = self._calculate_resize_deltas()
self.delta_des_vcpus = deltas['delta_vcpu']
def _validate_deploy(self):
"""Enforce validation rules specific to LPAR deployment."""
self._validate_host_has_available_res(
self.des_procs, self.procs_avail, self.res_name)
def _validate_active_resize(self):
"""Enforce validation rules specific to active resize."""
# Extract current values from existing LPAR.
curr_has_dedicated = self.cur_lpar_w.proc_config.has_dedicated
if curr_has_dedicated:
lpar_proc_config = self.cur_lpar_w.proc_config.dedicated_proc_cfg
curr_max_vcpus = lpar_proc_config.max
curr_min_vcpus = lpar_proc_config.min
else:
lpar_proc_config = self.cur_lpar_w.proc_config.shared_proc_cfg
curr_max_vcpus = lpar_proc_config.max_virtual
curr_min_vcpus = lpar_proc_config.min_virtual
curr_max_proc_units = lpar_proc_config.max_units
curr_min_proc_units = lpar_proc_config.min_units
# min/max cannot be changed when lpar is not powered off.
if (self.max_vcpus != curr_max_vcpus or
self.min_vcpus != curr_min_vcpus):
msg = (_("The virtual machine must be powered off before changing "
"the minimum or maximum processors. Power off virtual "
"machine %s and try again.") % self.cur_lpar_w.name)
raise ValidatorException(msg)
if not self.has_dedicated and not curr_has_dedicated:
curr_min_proc_units = round(float(curr_min_proc_units), 2)
curr_max_proc_units = round(float(curr_max_proc_units), 2)
if (round(self.max_proc_units, 2) != curr_max_proc_units or
round(self.min_proc_units, 2) != curr_min_proc_units):
msg = (_("The virtual machine must be powered off before "
"changing the minimum or maximum processor units. "
"Power off virtual machine %s and try again.") %
self.cur_lpar_w.name)
raise ValidatorException(msg)
# Processor compatibility mode cannot be changed when lpar is not
# powered off.
curr_proc_compat = self.cur_lpar_w.proc_compat_mode
curr_pend_proc_compat = self.cur_lpar_w.pending_proc_compat_mode
if self.proc_compat_mode is not None:
proc_compat = self.proc_compat_mode.lower()
if (proc_compat != curr_proc_compat.lower() and
(proc_compat != curr_pend_proc_compat.lower())):
# If requested was not the same as current, this is
# not supported when instance is not powered off.
msg = (_("The virtual machine must be powered off before "
"changing the processor compatibility mode. "
"Power off virtual machine %s and try again.") %
self.cur_lpar_w.name)
raise ValidatorException(msg)
# Processing mode cannot be changed when lpar is not powered off.
if self.has_dedicated != curr_has_dedicated:
msg = (_("The virtual machine must be powered off before changing "
"the processing mode. Power off virtual machine %s and "
"try again.") % self.cur_lpar_w.name)
raise ValidatorException(msg)
# Validations common for both active & inactive resizes.
self._validate_resize_common()
def _validate_inactive_resize(self):
"""Enforce validation rules specific to inactive resize."""
self._validate_resize_common()
def _validate_common(self):
"""Enforce operation agnostic validation rules."""
self._validate_host_max_allowed_procs_per_lpar()
self._validate_host_max_sys_procs_limit()
def _can_modify(self):
"""Checks proc dlpar and rmc state if LPAR not activated."""
modifiable, reason = self.cur_lpar_w.can_modify_proc()
if not modifiable:
LOG.error(reason)
raise ValidatorException(reason)
def _validate_host_max_allowed_procs_per_lpar(self):
if self.des_vcpus > self.max_procs_per_aix_linux_lpar:
ex_args = {'vcpus': self.des_vcpus,
'max_allowed': self.max_procs_per_aix_linux_lpar,
'instance_name': self.lpar_w.name}
msg = _("The desired processors (%(vcpus)d) cannot be above "
"the maximum allowed processors per partition "
"(%(max_allowed)d) for virtual machine "
"'%(instance_name)s'.") % ex_args
LOG.error(msg)
raise ValidatorException(msg)
def _validate_host_max_sys_procs_limit(self):
if self.max_vcpus > self.max_sys_procs_limit:
ex_args = {'vcpus': self.max_vcpus,
'max_allowed': self.max_sys_procs_limit,
'instance_name': self.lpar_w.name}
msg = _("The maximum processors (%(vcpus)d) cannot be above "
"the maximum system capacity processor limit "
"(%(max_allowed)d) for virtual machine "
"'%(instance_name)s'.") % ex_args
LOG.error(msg)
raise ValidatorException(msg)
def _validate_resize_common(self):
"""Validation rules common for both active and inactive resizes.
Helper method to enforce validation rules that are common for
both active and inactive resizes.
"""
self._validate_host_has_available_res(self.delta_des_vcpus,
self.procs_avail,
self.res_name)
def _calculate_resize_deltas(self):
"""Helper method to calculate the procs deltas for resize operation.
:return dict of processor deltas.
"""
deltas = {}
# Extract current values from existing LPAR.
curr_has_dedicated = self.cur_lpar_w.proc_config.has_dedicated
if curr_has_dedicated:
lpar_proc_config = self.cur_lpar_w.proc_config.dedicated_proc_cfg
curr_des_vcpus = lpar_proc_config.desired
else:
lpar_proc_config = self.cur_lpar_w.proc_config.shared_proc_cfg
curr_des_vcpus = lpar_proc_config.desired_virtual
curr_proc_units = lpar_proc_config.desired_units
# Calculate VCPU deltas
deltas['delta_vcpu'] = self.des_vcpus - curr_des_vcpus
# If this is dedicated processor mode, there are no proc_units.
if self.has_dedicated:
if not curr_has_dedicated and curr_proc_units is not None:
# Resize from Shared to Dedicated mode
deltas['delta_vcpu'] = (
round(self.des_vcpus - curr_proc_units, 2))
else:
if curr_has_dedicated:
# Resize from Dedicated to Shared mode
deltas['delta_vcpu'] = (
round(self.des_procs - curr_des_vcpus, 2))
else:
deltas['delta_vcpu'] = (
round(self.des_procs - curr_proc_units, 2))
return deltas
class CapabilitiesValidator(BaseValidator):
"""Capabilities Validator.
This class implements capabilities validation for lpars in the case of
deploy, inactive resize, and active resize.
Instance attributes populated by _populate_new_values
:attr srr_enabled: srr capability of the lpar
"""
def _populate_new_values(self):
"""Set newly desired resize attributes as instance attributes."""
self.srr_enabled = self.lpar_w.srr_enabled
def _validate_active_resize(self):
"""Enforce validation rules specific to active resize."""
# If not dynamic SRR toggle capable, Simplified Remote Restart
# capability cannot be changed unless lpar is powered off.
if self.cur_lpar_w.srr_enabled != self.srr_enabled:
dyn_srr_cap = self.host_w.get_capability('dynamic_srr_capable')
if not dyn_srr_cap:
msg = (_("The virtual machine must be powered off before "
"changing the simplified remote restart capability. "
"Power off virtual machine %s and try again.") %
self.cur_lpar_w.name)
raise ValidatorException(msg)
def _populate_resize_diffs(self):
"""Calculate lpar_w vs cur_lpar_w diffs and set as attributes."""
pass
def _validate_deploy(self):
"""Enforce validation rules specific to LPAR deployment."""
pass
def _validate_inactive_resize(self):
"""Enforce validation rules specific to inactive resize."""
pass
def _validate_common(self):
"""Enforce operation agnostic validation rules."""
pass
def _can_modify(self):
"""Check if capabilities may be modified."""
pass
|