Path: blob/master/src/applications/drydock/storage/DrydockResource.php
12256 views
<?php12final class DrydockResource extends DrydockDAO3implements4PhabricatorPolicyInterface,5PhabricatorConduitResultInterface {67protected $id;8protected $phid;9protected $blueprintPHID;10protected $status;11protected $until;12protected $type;13protected $attributes = array();14protected $capabilities = array();15protected $ownerPHID;1617private $blueprint = self::ATTACHABLE;18private $unconsumedCommands = self::ATTACHABLE;1920private $isAllocated = false;21private $isActivated = false;22private $activateWhenAllocated = false;23private $slotLocks = array();2425protected function getConfiguration() {26return array(27self::CONFIG_AUX_PHID => true,28self::CONFIG_SERIALIZATION => array(29'attributes' => self::SERIALIZATION_JSON,30'capabilities' => self::SERIALIZATION_JSON,31),32self::CONFIG_COLUMN_SCHEMA => array(33'ownerPHID' => 'phid?',34'status' => 'text32',35'type' => 'text64',36'until' => 'epoch?',37),38self::CONFIG_KEY_SCHEMA => array(39'key_type' => array(40'columns' => array('type', 'status'),41),42'key_blueprint' => array(43'columns' => array('blueprintPHID', 'status'),44),45),46) + parent::getConfiguration();47}4849public function generatePHID() {50return PhabricatorPHID::generateNewPHID(DrydockResourcePHIDType::TYPECONST);51}5253public function getResourceName() {54return $this->getBlueprint()->getResourceName($this);55}5657public function getAttribute($key, $default = null) {58return idx($this->attributes, $key, $default);59}6061public function getAttributesForTypeSpec(array $attribute_names) {62return array_select_keys($this->attributes, $attribute_names);63}6465public function setAttribute($key, $value) {66$this->attributes[$key] = $value;67return $this;68}6970public function getCapability($key, $default = null) {71return idx($this->capbilities, $key, $default);72}7374public function getInterface(DrydockLease $lease, $type) {75return $this->getBlueprint()->getInterface($this, $lease, $type);76}7778public function getBlueprint() {79return $this->assertAttached($this->blueprint);80}8182public function attachBlueprint(DrydockBlueprint $blueprint) {83$this->blueprint = $blueprint;84return $this;85}8687public function getUnconsumedCommands() {88return $this->assertAttached($this->unconsumedCommands);89}9091public function attachUnconsumedCommands(array $commands) {92$this->unconsumedCommands = $commands;93return $this;94}9596public function isReleasing() {97foreach ($this->getUnconsumedCommands() as $command) {98if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) {99return true;100}101}102103return false;104}105106public function setActivateWhenAllocated($activate) {107$this->activateWhenAllocated = $activate;108return $this;109}110111public function needSlotLock($key) {112$this->slotLocks[] = $key;113return $this;114}115116public function allocateResource() {117// We expect resources to have a pregenerated PHID, as they should have118// been created by a call to DrydockBlueprint->newResourceTemplate().119if (!$this->getPHID()) {120throw new Exception(121pht(122'Trying to allocate a resource with no generated PHID. Use "%s" to '.123'create new resource templates.',124'newResourceTemplate()'));125}126127$expect_status = DrydockResourceStatus::STATUS_PENDING;128$actual_status = $this->getStatus();129if ($actual_status != $expect_status) {130throw new Exception(131pht(132'Trying to allocate a resource from the wrong status. Status must '.133'be "%s", actually "%s".',134$expect_status,135$actual_status));136}137138if ($this->activateWhenAllocated) {139$new_status = DrydockResourceStatus::STATUS_ACTIVE;140} else {141$new_status = DrydockResourceStatus::STATUS_PENDING;142}143144$this->openTransaction();145146try {147DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);148$this->slotLocks = array();149} catch (DrydockSlotLockException $ex) {150$this->killTransaction();151152if ($this->getID()) {153$log_target = $this;154} else {155// If we don't have an ID, we have to log this on the blueprint, as the156// resource is not going to be saved so the PHID will vanish.157$log_target = $this->getBlueprint();158}159$log_target->logEvent(160DrydockSlotLockFailureLogType::LOGCONST,161array(162'locks' => $ex->getLockMap(),163));164165throw $ex;166}167168$this169->setStatus($new_status)170->save();171172$this->saveTransaction();173174$this->isAllocated = true;175176if ($new_status == DrydockResourceStatus::STATUS_ACTIVE) {177$this->didActivate();178}179180return $this;181}182183public function isAllocatedResource() {184return $this->isAllocated;185}186187public function activateResource() {188if (!$this->getID()) {189throw new Exception(190pht(191'Trying to activate a resource which has not yet been persisted.'));192}193194$expect_status = DrydockResourceStatus::STATUS_PENDING;195$actual_status = $this->getStatus();196if ($actual_status != $expect_status) {197throw new Exception(198pht(199'Trying to activate a resource from the wrong status. Status must '.200'be "%s", actually "%s".',201$expect_status,202$actual_status));203}204205$this->openTransaction();206207try {208DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);209$this->slotLocks = array();210} catch (DrydockSlotLockException $ex) {211$this->killTransaction();212213$this->logEvent(214DrydockSlotLockFailureLogType::LOGCONST,215array(216'locks' => $ex->getLockMap(),217));218219throw $ex;220}221222$this223->setStatus(DrydockResourceStatus::STATUS_ACTIVE)224->save();225226$this->saveTransaction();227228$this->isActivated = true;229230$this->didActivate();231232return $this;233}234235public function isActivatedResource() {236return $this->isActivated;237}238239public function scheduleUpdate($epoch = null) {240PhabricatorWorker::scheduleTask(241'DrydockResourceUpdateWorker',242array(243'resourcePHID' => $this->getPHID(),244'isExpireTask' => ($epoch !== null),245),246array(247'objectPHID' => $this->getPHID(),248'delayUntil' => ($epoch ? (int)$epoch : null),249));250}251252private function didActivate() {253$viewer = PhabricatorUser::getOmnipotentUser();254255$need_update = false;256257$commands = id(new DrydockCommandQuery())258->setViewer($viewer)259->withTargetPHIDs(array($this->getPHID()))260->withConsumed(false)261->execute();262if ($commands) {263$need_update = true;264}265266if ($need_update) {267$this->scheduleUpdate();268}269270$expires = $this->getUntil();271if ($expires) {272$this->scheduleUpdate($expires);273}274}275276public function logEvent($type, array $data = array()) {277$log = id(new DrydockLog())278->setEpoch(PhabricatorTime::getNow())279->setType($type)280->setData($data);281282$log->setResourcePHID($this->getPHID());283$log->setBlueprintPHID($this->getBlueprintPHID());284285return $log->save();286}287288public function getDisplayName() {289return pht('Drydock Resource %d', $this->getID());290}291292293/* -( Status )------------------------------------------------------------- */294295296public function getStatusObject() {297return DrydockResourceStatus::newStatusObject($this->getStatus());298}299300public function getStatusIcon() {301return $this->getStatusObject()->getIcon();302}303304public function getStatusColor() {305return $this->getStatusObject()->getColor();306}307308public function getStatusDisplayName() {309return $this->getStatusObject()->getDisplayName();310}311312public function canRelease() {313return $this->getStatusObject()->canRelease();314}315316public function canReceiveCommands() {317return $this->getStatusObject()->canReceiveCommands();318}319320public function isActive() {321return $this->getStatusObject()->isActive();322}323324325/* -( PhabricatorPolicyInterface )----------------------------------------- */326327328public function getCapabilities() {329return array(330PhabricatorPolicyCapability::CAN_VIEW,331PhabricatorPolicyCapability::CAN_EDIT,332);333}334335public function getPolicy($capability) {336return $this->getBlueprint()->getPolicy($capability);337}338339public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {340return $this->getBlueprint()->hasAutomaticCapability(341$capability,342$viewer);343}344345public function describeAutomaticCapability($capability) {346return pht('Resources inherit the policies of their blueprints.');347}348349350/* -( PhabricatorConduitResultInterface )---------------------------------- */351352353public function getFieldSpecificationsForConduit() {354return array(355id(new PhabricatorConduitSearchFieldSpecification())356->setKey('blueprintPHID')357->setType('phid')358->setDescription(pht('The blueprint which generated this resource.')),359id(new PhabricatorConduitSearchFieldSpecification())360->setKey('status')361->setType('map<string, wild>')362->setDescription(pht('Information about resource status.')),363);364}365366public function getFieldValuesForConduit() {367$status = $this->getStatus();368369return array(370'blueprintPHID' => $this->getBlueprintPHID(),371'status' => array(372'value' => $status,373'name' => DrydockResourceStatus::getNameForStatus($status),374),375);376}377378public function getConduitSearchAttachments() {379return array();380}381382}383384385