private function buildPropertyListView(DrydockBlueprint $blueprint, PhabricatorActionListView $actions) { $view = new PHUIPropertyListView(); $view->setActionList($actions); $view->addProperty(pht('Type'), $blueprint->getImplementation()->getBlueprintName()); return $view; }
private function buildResourceBox(DrydockBlueprint $blueprint) { $viewer = $this->getViewer(); $resources = id(new DrydockResourceQuery())->setViewer($viewer)->withBlueprintPHIDs(array($blueprint->getPHID()))->withStatuses(array(DrydockResourceStatus::STATUS_PENDING, DrydockResourceStatus::STATUS_ACTIVE))->setLimit(100)->execute(); $resource_list = id(new DrydockResourceListView())->setUser($viewer)->setResources($resources)->render()->setNoDataString(pht('This blueprint has no active resources.')); $id = $blueprint->getID(); $resources_uri = "blueprint/{$id}/resources/query/all/"; $resources_uri = $this->getApplicationURI($resources_uri); $resource_header = id(new PHUIHeaderView())->setHeader(pht('Active Resources'))->addActionLink(id(new PHUIButtonView())->setTag('a')->setHref($resources_uri)->setIconFont('fa-search')->setText(pht('View All Resources'))); return id(new PHUIObjectBoxView())->setHeader($resource_header)->setObjectList($resource_list); }
protected function loadPage() { $table = new DrydockBlueprint(); $conn_r = $table->establishConnection('r'); $data = queryfx_all($conn_r, 'SELECT blueprint.* FROM %T blueprint %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $blueprints = $table->loadAllFromArray($data); $implementations = DrydockBlueprintImplementation::getAllBlueprintImplementations(); foreach ($blueprints as $blueprint) { if (array_key_exists($blueprint->getClassName(), $implementations)) { $blueprint->attachImplementation($implementations[$blueprint->getClassName()]); } } return $blueprints; }
private function buildAuthorizationsBox(DrydockBlueprint $blueprint) { $viewer = $this->getViewer(); $limit = 25; // If there are pending authorizations against this blueprint, make sure // we show them first. $pending_authorizations = id(new DrydockAuthorizationQuery())->setViewer($viewer)->withBlueprintPHIDs(array($blueprint->getPHID()))->withObjectStates(array(DrydockAuthorization::OBJECTAUTH_ACTIVE))->withBlueprintStates(array(DrydockAuthorization::BLUEPRINTAUTH_REQUESTED))->setLimit($limit)->execute(); $all_authorizations = id(new DrydockAuthorizationQuery())->setViewer($viewer)->withBlueprintPHIDs(array($blueprint->getPHID()))->withObjectStates(array(DrydockAuthorization::OBJECTAUTH_ACTIVE))->withBlueprintStates(array(DrydockAuthorization::BLUEPRINTAUTH_REQUESTED, DrydockAuthorization::BLUEPRINTAUTH_AUTHORIZED))->setLimit($limit)->execute(); $authorizations = mpull($pending_authorizations, null, 'getPHID') + mpull($all_authorizations, null, 'getPHID'); $authorization_list = id(new DrydockAuthorizationListView())->setUser($viewer)->setAuthorizations($authorizations)->setNoDataString(pht('No objects have active authorizations to use this blueprint.')); $id = $blueprint->getID(); $authorizations_uri = "blueprint/{$id}/authorizations/query/all/"; $authorizations_uri = $this->getApplicationURI($authorizations_uri); $authorizations_header = id(new PHUIHeaderView())->setHeader(pht('Active Authorizations'))->addActionLink(id(new PHUIButtonView())->setTag('a')->setHref($authorizations_uri)->setIconFont('fa-search')->setText(pht('View All'))); return id(new PHUIObjectBoxView())->setHeader($authorizations_header)->setObjectList($authorization_list); }
protected function newEditableObject() { $viewer = $this->getViewer(); $blueprint = DrydockBlueprint::initializeNewBlueprint($viewer); $impl = $this->getBlueprintImplementation(); if ($impl) { $blueprint->setClassName(get_class($impl))->attachImplementation(clone $impl); } return $blueprint; }
protected function doWork() { $data = $this->getTaskData(); $lease = id(new DrydockLease())->loadOneWhere('id = %d', $data['lease']); if (!$lease) { return; } $type = $data['type']; $candidates = id(new DrydockResource())->loadAllWhere('type = %s AND status = %s', $type, DrydockResourceStatus::STATUS_OPEN); if ($candidates) { shuffle($candidates); $resource = head($candidates); } else { $blueprints = DrydockBlueprint::getAllBlueprintsForResource($type); foreach ($blueprints as $key => $blueprint) { if (!$blueprint->canAllocateResources()) { unset($blueprints[$key]); continue; } } if (!$blueprints) { $lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN); $lease->save(); DrydockBlueprint::writeLog(null, $lease, "There are no resources of type '{$type}' available, and no " . "blueprints which can allocate new ones."); return; } // TODO: Rank intelligently. shuffle($blueprints); $blueprint = head($blueprints); if (isset($data['synchronous'])) { $blueprint->makeSynchronous(); } $resource = $blueprint->allocateResource(); } $blueprint = $resource->getBlueprint(); $blueprint->acquireLease($resource, $lease); }
/** * @task activate */ private function validateActivatedLease(DrydockBlueprint $blueprint, DrydockResource $resource, DrydockLease $lease) { if (!$lease->isActivatedLease()) { throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: it ' . 'returned from "%s" without activating a lease.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'acquireLease()')); } }
private function loadFreeBindings(DrydockBlueprint $blueprint) { if ($this->freeBindings === null) { $viewer = PhabricatorUser::getOmnipotentUser(); $pool = id(new DrydockResourceQuery())->setViewer($viewer)->withBlueprintPHIDs(array($blueprint->getPHID()))->withStatuses(array(DrydockResourceStatus::STATUS_PENDING, DrydockResourceStatus::STATUS_OPEN, DrydockResourceStatus::STATUS_CLOSED))->execute(); $allocated_phids = array(); foreach ($pool as $resource) { $allocated_phids[] = $resource->getAttribute('almanacDevicePHID'); } $allocated_phids = array_fuse($allocated_phids); $services = $this->loadServices($blueprint); $bindings = $this->loadAllBindings($services); $free = array(); foreach ($bindings as $binding) { if (empty($allocated_phids[$binding->getPHID()])) { $free[] = $binding; } } $this->freeBindings = $free; } return $this->freeBindings; }
/** * @task activate */ 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()')); } }
/** * Get the effective concurrent resource limit for this blueprint. * * @param DrydockBlueprint Blueprint to get the limit for. * @return int|null Limit, or `null` for no limit. */ protected function getConcurrentResourceLimit(DrydockBlueprint $blueprint) { if ($this->shouldUseConcurrentResourceLimit()) { $limit = $blueprint->getFieldValue('allocator.limit'); $limit = (int) $limit; if ($limit > 0) { return $limit; } else { return null; } } return null; }
/** * 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 lease */ 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) { // TODO: Destroy the lease? 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()')); } }
<?php echo pht('Adding names to Drydock blueprints.') . "\n"; $table = new DrydockBlueprint(); $conn_w = $table->establishConnection('w'); $iterator = new LiskMigrationIterator($table); foreach ($iterator as $blueprint) { $id = $blueprint->getID(); echo pht('Populating blueprint %d...', $id) . "\n"; if (!strlen($blueprint->getBlueprintName())) { queryfx($conn_w, 'UPDATE %T SET blueprintName = %s WHERE id = %d', $table->getTableName(), pht('Blueprint %s', $id), $id); } } echo pht('Done.') . "\n";
/** * 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 = 1; // 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. $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 handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); if ($id) { $blueprint = id(new DrydockBlueprintQuery())->setViewer($viewer)->withIDs(array($id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$blueprint) { return new Aphront404Response(); } $impl = $blueprint->getImplementation(); $cancel_uri = $this->getApplicationURI('blueprint/' . $id . '/'); } else { $this->requireApplicationCapability(DrydockCreateBlueprintsCapability::CAPABILITY); $class = $request->getStr('class'); $impl = DrydockBlueprintImplementation::getNamedImplementation($class); if (!$impl || !$impl->isEnabled()) { return new Aphront400Response(); } $blueprint = DrydockBlueprint::initializeNewBlueprint($viewer); $blueprint->setClassName($class); $cancel_uri = $this->getApplicationURI('blueprint/'); } $field_list = PhabricatorCustomField::getObjectFields($blueprint, PhabricatorCustomField::ROLE_EDIT); $field_list->setViewer($viewer)->readFieldsFromStorage($blueprint); $v_name = $blueprint->getBlueprintName(); $e_name = true; $errors = array(); $validation_exception = null; if ($request->isFormPost()) { $v_view_policy = $request->getStr('viewPolicy'); $v_edit_policy = $request->getStr('editPolicy'); $v_name = $request->getStr('name'); if (!strlen($v_name)) { $e_name = pht('Required'); $errors[] = pht('You must name this blueprint.'); } if (!$errors) { $xactions = array(); $xactions = $field_list->buildFieldTransactionsFromRequest(new DrydockBlueprintTransaction(), $request); $xactions[] = id(new DrydockBlueprintTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($v_view_policy); $xactions[] = id(new DrydockBlueprintTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)->setNewValue($v_edit_policy); $xactions[] = id(new DrydockBlueprintTransaction())->setTransactionType(DrydockBlueprintTransaction::TYPE_NAME)->setNewValue($v_name); $editor = id(new DrydockBlueprintEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true); try { $editor->applyTransactions($blueprint, $xactions); $id = $blueprint->getID(); $save_uri = $this->getApplicationURI("blueprint/{$id}/"); return id(new AphrontRedirectResponse())->setURI($save_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; } } } $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($blueprint)->execute(); $form = id(new AphrontFormView())->setUser($viewer)->addHiddenInput('class', $request->getStr('class'))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Name'))->setName('name')->setValue($v_name)->setError($e_name))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Blueprint Type'))->setValue($impl->getBlueprintName()))->appendChild(id(new AphrontFormPolicyControl())->setName('viewPolicy')->setPolicyObject($blueprint)->setCapability(PhabricatorPolicyCapability::CAN_VIEW)->setPolicies($policies))->appendChild(id(new AphrontFormPolicyControl())->setName('editPolicy')->setPolicyObject($blueprint)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setPolicies($policies)); $field_list->appendFieldsToForm($form); $crumbs = $this->buildApplicationCrumbs(); if ($blueprint->getID()) { $title = pht('Edit Blueprint'); $header = pht('Edit Blueprint %d', $blueprint->getID()); $crumbs->addTextCrumb(pht('Blueprint %d', $blueprint->getID())); $crumbs->addTextCrumb(pht('Edit')); $submit = pht('Save Blueprint'); } else { $title = pht('New Blueprint'); $header = pht('New Blueprint'); $crumbs->addTextCrumb(pht('New Blueprint')); $submit = pht('Create Blueprint'); } $form->appendChild(id(new AphrontFormSubmitControl())->setValue($submit)->addCancelButton($cancel_uri)); $box = id(new PHUIObjectBoxView())->setHeaderText($header)->setValidationException($validation_exception)->setFormErrors($errors)->setForm($form); return $this->buildApplicationPage(array($crumbs, $box), array('title' => $title)); }
private function loadFreeBindings(DrydockBlueprint $blueprint) { if ($this->freeBindings === null) { $viewer = PhabricatorUser::getOmnipotentUser(); $pool = id(new DrydockResourceQuery())->setViewer($viewer)->withBlueprintPHIDs(array($blueprint->getPHID()))->withStatuses(array(DrydockResourceStatus::STATUS_PENDING, DrydockResourceStatus::STATUS_ACTIVE, DrydockResourceStatus::STATUS_BROKEN, DrydockResourceStatus::STATUS_RELEASED))->execute(); $allocated_phids = array(); foreach ($pool as $resource) { $allocated_phids[] = $resource->getAttribute('almanacBindingPHID'); } $allocated_phids = array_fuse($allocated_phids); $services = $this->loadServices($blueprint); $bindings = $this->loadAllBindings($services); $free = array(); foreach ($bindings as $binding) { // Don't consider disabled bindings to be available. if ($binding->getIsDisabled()) { continue; } if (empty($allocated_phids[$binding->getPHID()])) { $free[] = $binding; } } $this->freeBindings = $free; } return $this->freeBindings; }