protected function yieldIfExpiringResource(DrydockResource $resource) { if (!$resource->canReceiveCommands()) { return; } $this->yieldIfExpiring($resource->getUntil()); }
public function getInterface(DrydockResource $resource, DrydockLease $lease, $type) { switch ($type) { case 'command': return $this->loadLease($resource->getAttribute('lease.host'))->getInterface($type); } throw new Exception("No interface of type '{$type}'."); }
protected function loadPage() { $table = new DrydockResource(); $conn_r = $table->establishConnection('r'); $data = queryfx_all($conn_r, 'SELECT resource.* FROM %T resource %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $resources = $table->loadAllFromArray($data); return $resources; }
private function buildLeaseBox(DrydockResource $resource) { $viewer = $this->getViewer(); $leases = id(new DrydockLeaseQuery())->setViewer($viewer)->withResourcePHIDs(array($resource->getPHID()))->withStatuses(array(DrydockLeaseStatus::STATUS_PENDING, DrydockLeaseStatus::STATUS_ACQUIRED, DrydockLeaseStatus::STATUS_ACTIVE))->setLimit(100)->execute(); $id = $resource->getID(); $leases_uri = "resource/{$id}/leases/query/all/"; $leases_uri = $this->getApplicationURI($leases_uri); $lease_header = id(new PHUIHeaderView())->setHeader(pht('Active Leases'))->addActionLink(id(new PHUIButtonView())->setTag('a')->setHref($leases_uri)->setIconFont('fa-search')->setText(pht('View All Leases'))); $lease_list = id(new DrydockLeaseListView())->setUser($viewer)->setLeases($leases)->render()->setNoDataString(pht('This resource has no active leases.')); return id(new PHUIObjectBoxView())->setHeader($lease_header)->setObjectList($lease_list); }
private function destroyResource(DrydockResource $resource) { $status = $resource->getStatus(); switch ($status) { case DrydockResourceStatus::STATUS_RELEASED: case DrydockResourceStatus::STATUS_BROKEN: break; default: throw new PhabricatorWorkerPermanentFailureException(pht('Unable to destroy resource ("%s"), resource has the wrong ' . 'status ("%s").', $resource->getPHID(), $status)); } $blueprint = $resource->getBlueprint(); $blueprint->destroyResource($resource); $resource->setStatus(DrydockResourceStatus::STATUS_DESTROYED)->save(); }
public function getInterface(DrydockResource $resource, DrydockLease $lease, $type) { switch ($type) { case 'command': return id(new DrydockSSHCommandInterface())->setConfiguration(array('host' => $resource->getAttribute('host'), 'port' => $resource->getAttribute('port'), 'credential' => $resource->getAttribute('credential'), 'platform' => $resource->getAttribute('platform')))->setWorkingDirectory($lease->getAttribute('path')); case 'filesystem': return id(new DrydockSFTPFilesystemInterface())->setConfiguration(array('host' => $resource->getAttribute('host'), 'port' => $resource->getAttribute('port'), 'credential' => $resource->getAttribute('credential'))); } throw new Exception(pht("No interface of type '%s'.", $type)); }
private function buildPropertyListView(DrydockResource $resource, PhabricatorActionListView $actions) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView())->setActionList($actions); $status = $resource->getStatus(); $status = DrydockResourceStatus::getNameForStatus($status); $view->addProperty(pht('Status'), $status); $view->addProperty(pht('Resource Type'), $resource->getType()); $view->addProperty(pht('Blueprint'), $viewer->renderHandle($resource->getBlueprintPHID())); $attributes = $resource->getAttributes(); if ($attributes) { $view->addSectionHeader(pht('Attributes'), 'fa-list-ul'); foreach ($attributes as $key => $value) { $view->addProperty($key, $value); } } return $view; }
public function getInterface(DrydockResource $resource, DrydockLease $lease, $type) { switch ($type) { case 'webroot': $iface = new DrydockApacheWebrootInterface(); $iface->setConfiguration(array('uri' => $lease->getAttribute('uri'))); return $iface; case 'command': $host_lease_id = $resource->getAttribute('lease.host'); $host_lease = id(new DrydockLease())->load($host_lease_id); $host_lease->attachResource($host_lease->loadResource()); return $host_lease->getInterface($type); } throw new Exception("No interface of type '{$type}'."); }
public function getInterface(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease, $type) { $viewer = PhabricatorUser::getOmnipotentUser(); switch ($type) { case DrydockCommandInterface::INTERFACE_TYPE: $credential_phid = $blueprint->getFieldValue('credentialPHID'); $binding_phid = $resource->getAttribute('almanacBindingPHID'); $binding = id(new AlmanacBindingQuery())->setViewer($viewer)->withPHIDs(array($binding_phid))->executeOne(); if (!$binding) { // TODO: This is probably a permanent failure, destroy this resource? throw new Exception(pht('Unable to load binding "%s" to create command interface.', $binding_phid)); } $interface = $binding->getInterface(); return id(new DrydockSSHCommandInterface())->setConfig('credentialPHID', $credential_phid)->setConfig('host', $interface->getAddress())->setConfig('port', $interface->getPort()); } }
private function releaseResource(DrydockResource $resource) { if ($resource->getStatus() != DrydockResourceStatus::STATUS_ACTIVE) { // If we had multiple release commands // This command is only meaningful to resources in the "Open" state. return; } $viewer = $this->getViewer(); $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); $resource->openTransaction(); $resource->setStatus(DrydockResourceStatus::STATUS_RELEASED)->save(); // TODO: Hold slot locks until destruction? DrydockSlotLock::releaseLocks($resource->getPHID()); $resource->saveTransaction(); $statuses = array(DrydockLeaseStatus::STATUS_PENDING, DrydockLeaseStatus::STATUS_ACQUIRED, DrydockLeaseStatus::STATUS_ACTIVE); $leases = id(new DrydockLeaseQuery())->setViewer($viewer)->withResourcePHIDs(array($resource->getPHID()))->withStatuses($statuses)->execute(); foreach ($leases as $lease) { $command = DrydockCommand::initializeNewCommand($viewer)->setTargetPHID($lease->getPHID())->setAuthorPHID($drydock_phid)->setCommand(DrydockCommand::COMMAND_RELEASE)->save(); $lease->scheduleUpdate(); } PhabricatorWorker::scheduleTask('DrydockResourceDestroyWorker', array('resourcePHID' => $resource->getPHID()), array('objectPHID' => $resource->getPHID())); }
private function loadHostLease(DrydockResource $resource) { $viewer = PhabricatorUser::getOmnipotentUser(); $lease_phid = $resource->getAttribute('host.leasePHID'); $lease = id(new DrydockLeaseQuery())->setViewer($viewer)->withPHIDs(array($lease_phid))->executeOne(); if (!$lease) { throw new Exception(pht('Unable to load lease ("%s").', $lease_phid)); } return $lease; }
/** * @task destroy */ private function destroyResource(DrydockResource $resource) { $blueprint = $resource->getBlueprint(); $blueprint->destroyResource($resource); DrydockSlotLock::releaseLocks($resource->getPHID()); $resource->setStatus(DrydockResourceStatus::STATUS_DESTROYED)->save(); }
/** * Apply standard limits on resource allocation rate. * * @param DrydockBlueprint The blueprint requesting an allocation. * @return bool True if further allocations should be limited. */ protected function shouldLimitAllocatingPoolSize(DrydockBlueprint $blueprint) { // TODO: If this mechanism sticks around, these values should be // configurable by the blueprint implementation. // Limit on total number of active resources. $total_limit = $this->getConcurrentResourceLimit($blueprint); // Always allow at least this many allocations to be in flight at once. $min_allowed = 1; // Allow this fraction of allocating resources as a fraction of active // resources. $growth_factor = 0.25; $resource = new DrydockResource(); $conn_r = $resource->establishConnection('r'); $counts = queryfx_all($conn_r, 'SELECT status, COUNT(*) N FROM %T WHERE blueprintPHID = %s AND status != %s GROUP BY status', $resource->getTableName(), $blueprint->getPHID(), DrydockResourceStatus::STATUS_DESTROYED); $counts = ipull($counts, 'N', 'status'); $n_alloc = idx($counts, DrydockResourceStatus::STATUS_PENDING, 0); $n_active = idx($counts, DrydockResourceStatus::STATUS_ACTIVE, 0); $n_broken = idx($counts, DrydockResourceStatus::STATUS_BROKEN, 0); $n_released = idx($counts, DrydockResourceStatus::STATUS_RELEASED, 0); // If we're at the limit on total active resources, limit additional // allocations. if ($total_limit !== null) { $n_total = $n_alloc + $n_active + $n_broken + $n_released; if ($n_total >= $total_limit) { return true; } } // If the number of in-flight allocations is fewer than the minimum number // of allowed allocations, don't impose a limit. if ($n_alloc < $min_allowed) { return false; } $allowed_alloc = (int) ceil($n_active * $growth_factor); // If the number of in-flight allocation is fewer than the number of // allowed allocations according to the pool growth factor, don't impose // a limit. if ($n_alloc < $allowed_alloc) { return false; } return true; }
public function activateOnResource(DrydockResource $resource) { $expect_status = DrydockLeaseStatus::STATUS_ACQUIRED; $actual_status = $this->getStatus(); if ($actual_status != $expect_status) { throw new Exception(pht('Trying to activate a lease which has the wrong status: status ' . 'must be "%s", actually "%s".', $expect_status, $actual_status)); } if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) { // TODO: Be stricter about this? throw new Exception(pht('Trying to activate a lease on a pending resource.')); } $this->openTransaction(); $this->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)->save(); DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks); $this->slotLocks = array(); $this->saveTransaction(); $this->isActivated = true; $this->didActivate(); return $this; }
private function validateActivatedResource(DrydockBlueprint $blueprint, DrydockResource $resource) { if (!$resource->isActivatedResource()) { throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: %s ' . 'must actually allocate the resource it returns.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'allocateResource()')); } }
protected function newResourceTemplate($name) { $resource = new DrydockResource(); $resource->setBlueprintClass(get_class($this)); $resource->setType($this->getType()); $resource->setStatus(DrydockResourceStatus::STATUS_PENDING); $resource->setName($name); $resource->save(); $this->activeResource = $resource; $this->log('New Template'); return $resource; }
/** * @task log */ public static function writeLog(DrydockResource $resource = null, DrydockLease $lease = null, $message = null) { $log = id(new DrydockLog())->setEpoch(time())->setMessage($message); if ($resource) { $log->setResourceID($resource->getID()); } if ($lease) { $log->setLeaseID($lease->getID()); } $log->save(); }
protected function reclaimResource(DrydockResource $resource, DrydockLease $lease) { $viewer = $this->getViewer(); $command = DrydockCommand::initializeNewCommand($viewer)->setTargetPHID($resource->getPHID())->setAuthorPHID($lease->getPHID())->setCommand(DrydockCommand::COMMAND_RECLAIM)->save(); $resource->scheduleUpdate(); return $this; }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $resource = new DrydockResource(); $json = new PhutilJSON(); $err_attributes = true; $err_capabilities = true; $json_attributes = $json->encodeFormatted($resource->getAttributes()); $json_capabilities = $json->encodeFormatted($resource->getCapabilities()); $errors = array(); if ($request->isFormPost()) { $raw_attributes = $request->getStr('attributes'); $attributes = json_decode($raw_attributes, true); if (!is_array($attributes)) { $err_attributes = 'Invalid'; $errors[] = 'Enter attributes as a valid JSON object.'; $json_attributes = $raw_attributes; } else { $resource->setAttributes($attributes); $json_attributes = $json->encodeFormatted($attributes); $err_attributes = null; } $raw_capabilities = $request->getStr('capabilities'); $capabilities = json_decode($raw_capabilities, true); if (!is_array($capabilities)) { $err_capabilities = 'Invalid'; $errors[] = 'Enter capabilities as a valid JSON object.'; $json_capabilities = $raw_capabilities; } else { $resource->setCapabilities($capabilities); $json_capabilities = $json->encodeFormatted($capabilities); $err_capabilities = null; } $resource->setBlueprintClass($request->getStr('blueprint')); $resource->setType($resource->getBlueprint()->getType()); $resource->setOwnerPHID($user->getPHID()); $resource->setName($request->getStr('name')); if (!$errors) { $resource->save(); return id(new AphrontRedirectResponse())->setURI('/drydock/resource/'); } } $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setTitle('Form Errors'); $error_view->setErrors($errors); } $blueprints = id(new PhutilSymbolLoader())->setType('class')->setAncestorClass('DrydockBlueprint')->selectAndLoadSymbols(); $blueprints = ipull($blueprints, 'name', 'name'); $panel = new AphrontPanelView(); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->setHeader('Allocate Drydock Resource'); $form = id(new AphrontFormView())->setUser($request->getUser())->appendChild(id(new AphrontFormTextControl())->setLabel('Name')->setName('name')->setValue($resource->getName()))->appendChild(id(new AphrontFormSelectControl())->setLabel('Blueprint')->setOptions($blueprints)->setName('blueprint')->setValue($resource->getBlueprintClass()))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Attributes')->setName('attributes')->setValue($json_attributes)->setError($err_attributes)->setCaption('Specify attributes in JSON.'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Capabilities')->setName('capabilities')->setValue($json_capabilities)->setError($err_capabilities)->setCaption('Specify capabilities in JSON.'))->appendChild(id(new AphrontFormSubmitControl())->setValue('Allocate Resource')); $panel->appendChild($form); return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Allocate Resource')); }
/** * Make sure that a lease was really acquired properly. * * @param DrydockBlueprint Blueprint which created the resource. * @param DrydockResource Resource which was acquired. * @param DrydockLease The lease which was supposedly acquired. * @return void * @task acquire */ private function validateAcquiredLease(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { if (!$lease->isAcquiredLease()) { throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: it ' . 'returned from "%s" without acquiring a lease.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'acquireLease()')); } $lease_phid = $lease->getResourcePHID(); $resource_phid = $resource->getPHID(); if ($lease_phid !== $resource_phid) { throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: it ' . 'returned from "%s" with a lease acquired on the wrong resource.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'acquireLease()')); } }
public function getInterface(DrydockResource $resource, DrydockLease $lease, $type) { switch ($type) { case 'command': $ssh = new DrydockSSHCommandInterface(); $ssh->setConfiguration(array('host' => $resource->getAttribute('host'), 'user' => $resource->getAttribute('user'), 'ssh-keyfile' => $resource->getAttribute('ssh-keyfile'))); return $ssh; } throw new Exception("No interface of type '{$type}'."); }