mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Build AlmanacInterface
Summary: Ref T5833. An interface is an IP (maybe v4, maybe v6) and port on a specified network (public internet, VPN, NAT block, etc). Test Plan: See screenshots. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5833 Differential Revision: https://secure.phabricator.com/D10718
This commit is contained in:
parent
3e704f6c78
commit
3e516e40f3
12 changed files with 835 additions and 0 deletions
13
resources/sql/autopatches/20141016.almanac.interface.sql
Normal file
13
resources/sql/autopatches/20141016.almanac.interface.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE {$NAMESPACE}_almanac.almanac_interface (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
devicePHID VARBINARY(64) NOT NULL,
|
||||
networkPHID VARBINARY(64) NOT NULL,
|
||||
address VARCHAR(128) NOT NULL COLLATE utf8_bin,
|
||||
port INT UNSIGNED NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
KEY `key_location` (networkPHID, address, port),
|
||||
KEY `key_device` (devicePHID)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
|
@ -9,6 +9,7 @@
|
|||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' => array(
|
||||
'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
|
||||
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
|
||||
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
|
||||
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
|
||||
|
@ -28,6 +29,11 @@ phutil_register_library_map(array(
|
|||
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
|
||||
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
||||
'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.php',
|
||||
'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
|
||||
'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
|
||||
'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
|
||||
'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
|
||||
'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
|
||||
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
|
||||
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
||||
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
||||
|
@ -2940,6 +2946,7 @@ phutil_register_library_map(array(
|
|||
'require_celerity_resource' => 'applications/celerity/api.php',
|
||||
),
|
||||
'xmap' => array(
|
||||
'AlmanacAddress' => 'Phobject',
|
||||
'AlmanacConduitUtil' => 'Phobject',
|
||||
'AlmanacConsoleController' => 'AlmanacController',
|
||||
'AlmanacController' => 'PhabricatorController',
|
||||
|
@ -2962,6 +2969,14 @@ phutil_register_library_map(array(
|
|||
'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'AlmanacDeviceViewController' => 'AlmanacDeviceController',
|
||||
'AlmanacInterface' => array(
|
||||
'AlmanacDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
|
||||
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
|
||||
'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'AlmanacInterfaceTableView' => 'AphrontView',
|
||||
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
|
||||
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'AlmanacNames' => 'Phobject',
|
||||
|
|
|
@ -44,6 +44,9 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
|||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
||||
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
||||
),
|
||||
'interface/' => array(
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController',
|
||||
),
|
||||
'network/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
|
||||
|
|
|
@ -35,6 +35,8 @@ final class AlmanacDeviceViewController
|
|||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
|
||||
$interfaces = $this->buildInterfaceList($device);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($device->getName());
|
||||
|
||||
|
@ -53,6 +55,7 @@ final class AlmanacDeviceViewController
|
|||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
$interfaces,
|
||||
$xaction_view,
|
||||
),
|
||||
array(
|
||||
|
@ -92,4 +95,48 @@ final class AlmanacDeviceViewController
|
|||
return $actions;
|
||||
}
|
||||
|
||||
private function buildInterfaceList(AlmanacDevice $device) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $device->getID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$device,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$interfaces = id(new AlmanacInterfaceQuery())
|
||||
->setViewer($viewer)
|
||||
->withDevicePHIDs(array($device->getPHID()))
|
||||
->execute();
|
||||
|
||||
$phids = array();
|
||||
foreach ($interfaces as $interface) {
|
||||
$phids[] = $interface->getNetworkPHID();
|
||||
$phids[] = $interface->getDevicePHID();
|
||||
}
|
||||
$handles = $this->loadViewerHandles($phids);
|
||||
|
||||
$table = id(new AlmanacInterfaceTableView())
|
||||
->setUser($viewer)
|
||||
->setInterfaces($interfaces)
|
||||
->setHandles($handles);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Device Interfaces'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($this->getApplicationURI("interface/edit/?deviceID={$id}"))
|
||||
->setWorkflow(!$can_edit)
|
||||
->setDisabled(!$can_edit)
|
||||
->setText(pht('Add Interface'))
|
||||
->setIcon(
|
||||
id(new PHUIIconView())
|
||||
->setIconFont('fa-plus')));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($table);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterfaceEditController
|
||||
extends AlmanacDeviceController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if ($id) {
|
||||
$interface = id(new AlmanacInterfaceQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$interface) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$device = $interface->getDevice();
|
||||
|
||||
$is_new = false;
|
||||
$title = pht('Edit Interface');
|
||||
$save_button = pht('Save Changes');
|
||||
} else {
|
||||
$device = id(new AlmanacDeviceQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getStr('deviceID')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$device) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$interface = AlmanacInterface::initializeNewInterface();
|
||||
$is_new = true;
|
||||
|
||||
$title = pht('Create Interface');
|
||||
$save_button = pht('Create Interface');
|
||||
}
|
||||
|
||||
$device_uri = $device->getURI();
|
||||
$cancel_uri = $device_uri;
|
||||
|
||||
$v_network = $interface->getNetworkPHID();
|
||||
|
||||
$v_address = $interface->getAddress();
|
||||
$e_address = true;
|
||||
|
||||
$v_port = $interface->getPort();
|
||||
|
||||
$validation_exception = null;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$v_network = $request->getStr('networkPHID');
|
||||
$v_address = $request->getStr('address');
|
||||
$v_port = $request->getStr('port');
|
||||
|
||||
$type_interface = AlmanacDeviceTransaction::TYPE_INTERFACE;
|
||||
|
||||
$address = AlmanacAddress::newFromParts($v_network, $v_address, $v_port);
|
||||
|
||||
$xaction = id(new AlmanacDeviceTransaction())
|
||||
->setTransactionType($type_interface)
|
||||
->setNewValue($address->toDictionary());
|
||||
|
||||
if ($interface->getID()) {
|
||||
$xaction->setOldValue(array(
|
||||
'id' => $interface->getID(),
|
||||
) + $interface->toAddress()->toDictionary());
|
||||
} else {
|
||||
$xaction->setOldValue(array());
|
||||
}
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = $xaction;
|
||||
|
||||
$editor = id(new AlmanacDeviceEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
try {
|
||||
$editor->applyTransactions($device, $xactions);
|
||||
|
||||
$device_uri = $device->getURI();
|
||||
return id(new AphrontRedirectResponse())->setURI($device_uri);
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
$e_address = $ex->getShortMessage($type_interface);
|
||||
}
|
||||
}
|
||||
|
||||
$networks = id(new AlmanacNetworkQuery())
|
||||
->setViewer($viewer)
|
||||
->execute();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Network'))
|
||||
->setName('networkPHID')
|
||||
->setValue($v_network)
|
||||
->setOptions(mpull($networks, 'getName', 'getPHID')))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Address'))
|
||||
->setName('address')
|
||||
->setValue($v_address)
|
||||
->setError($e_address))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Port'))
|
||||
->setName('port')
|
||||
->setValue($v_port)
|
||||
->setError($e_address))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton($cancel_uri)
|
||||
->setValue($save_button));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setValidationException($validation_exception)
|
||||
->setHeaderText($title)
|
||||
->appendChild($form);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($device->getName(), $device_uri);
|
||||
if ($is_new) {
|
||||
$crumbs->addTextCrumb(pht('Create Interface'));
|
||||
} else {
|
||||
$crumbs->addTextCrumb(pht('Edit Interface'));
|
||||
}
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,7 @@ final class AlmanacDeviceEditor
|
|||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = AlmanacDeviceTransaction::TYPE_NAME;
|
||||
$types[] = AlmanacDeviceTransaction::TYPE_INTERFACE;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
|
@ -38,6 +39,7 @@ final class AlmanacDeviceEditor
|
|||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case AlmanacDeviceTransaction::TYPE_NAME:
|
||||
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||
return $xaction->getNewValue();
|
||||
}
|
||||
|
||||
|
@ -52,6 +54,7 @@ final class AlmanacDeviceEditor
|
|||
case AlmanacDeviceTransaction::TYPE_NAME:
|
||||
$object->setName($xaction->getNewValue());
|
||||
return;
|
||||
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return;
|
||||
|
@ -69,6 +72,32 @@ final class AlmanacDeviceEditor
|
|||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return;
|
||||
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||
$old = $xaction->getOldValue();
|
||||
if ($old) {
|
||||
$interface = id(new AlmanacInterfaceQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withIDs(array($old['id']))
|
||||
->executeOne();
|
||||
if (!$interface) {
|
||||
throw new Exception(pht('Unable to load interface!'));
|
||||
}
|
||||
} else {
|
||||
$interface = AlmanacInterface::initializeNewInterface()
|
||||
->setDevicePHID($object->getPHID());
|
||||
}
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
if ($new) {
|
||||
$interface
|
||||
->setNetworkPHID($new['networkPHID'])
|
||||
->setAddress($new['address'])
|
||||
->setPort((int)$new['port'])
|
||||
->save();
|
||||
} else {
|
||||
$interface->delete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||
|
@ -134,6 +163,125 @@ final class AlmanacDeviceEditor
|
|||
}
|
||||
|
||||
break;
|
||||
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||
// We want to make sure that all the affected networks are visible to
|
||||
// the actor, any edited interfaces exist, and that the actual address
|
||||
// components are valid.
|
||||
|
||||
$network_phids = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$old = $xaction->getOldValue();
|
||||
$new = $xaction->getNewValue();
|
||||
if ($old) {
|
||||
$network_phids[] = $old['networkPHID'];
|
||||
}
|
||||
if ($new) {
|
||||
$network_phids[] = $new['networkPHID'];
|
||||
|
||||
$address = $new['address'];
|
||||
if (!strlen($address)) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('Interfaces must have an address.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
} else {
|
||||
// TODO: Validate addresses, but IPv6 addresses are not trival
|
||||
// to validate.
|
||||
}
|
||||
|
||||
$port = $new['port'];
|
||||
if (!strlen($port)) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('Interfaces must have a port.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
} else if ((int)$port < 1 || (int)$port > 65535) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'Port numbers must be between 1 and 65535, inclusive.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($network_phids) {
|
||||
$networks = id(new AlmanacNetworkQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs($network_phids)
|
||||
->execute();
|
||||
$networks = mpull($networks, null, 'getPHID');
|
||||
} else {
|
||||
$networks = array();
|
||||
}
|
||||
|
||||
$addresses = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$old = $xaction->getOldValue();
|
||||
if ($old) {
|
||||
$network = idx($networks, $old['networkPHID']);
|
||||
if (!$network) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'You can not edit an interface which belongs to a '.
|
||||
'nonexistent or restricted network.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
}
|
||||
|
||||
$addresses[] = $old['id'];
|
||||
}
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
if ($new) {
|
||||
$network = idx($networks, $new['networkPHID']);
|
||||
if (!$network) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'You can not add an interface on a nonexistent or '.
|
||||
'restricted network.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($addresses) {
|
||||
$interfaces = id(new AlmanacInterfaceQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withDevicePHIDs(array($object->getPHID()))
|
||||
->withIDs($addresses)
|
||||
->execute();
|
||||
$interfaces = mpull($interfaces, null, 'getID');
|
||||
} else {
|
||||
$interfaces = array();
|
||||
}
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$old = $xaction->getOldValue();
|
||||
if ($old) {
|
||||
$interface = idx($interfaces, $old['id']);
|
||||
if (!$interface) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('You can not edit an invalid or restricted interface.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
|
38
src/applications/almanac/phid/AlmanacInterfacePHIDType.php
Normal file
38
src/applications/almanac/phid/AlmanacInterfacePHIDType.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'AINT';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Almanac Interface');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new AlmanacInterface();
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new AlmanacInterfaceQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$interface = $objects[$phid];
|
||||
|
||||
$id = $interface->getID();
|
||||
|
||||
$handle->setObjectName(pht('Interface %d', $id));
|
||||
$handle->setName(pht('Interface %d', $id));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
139
src/applications/almanac/query/AlmanacInterfaceQuery.php
Normal file
139
src/applications/almanac/query/AlmanacInterfaceQuery.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterfaceQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $networkPHIDs;
|
||||
private $devicePHIDs;
|
||||
private $addresses;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withNetworkPHIDs(array $phids) {
|
||||
$this->networkPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withDevicePHIDs(array $phids) {
|
||||
$this->devicePHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAddresses(array $addresses) {
|
||||
$this->addresses = $addresses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new AlmanacInterface();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $interfaces) {
|
||||
$network_phids = mpull($interfaces, 'getNetworkPHID');
|
||||
$device_phids = mpull($interfaces, 'getDevicePHID');
|
||||
|
||||
$networks = id(new AlmanacNetworkQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs($network_phids)
|
||||
->execute();
|
||||
$networks = mpull($networks, null, 'getPHID');
|
||||
|
||||
$devices = id(new AlmanacDeviceQuery())
|
||||
->setParentQuery($this)
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs($device_phids)
|
||||
->execute();
|
||||
$devices = mpull($devices, null, 'getPHID');
|
||||
|
||||
foreach ($interfaces as $key => $interface) {
|
||||
$network = idx($networks, $interface->getNetworkPHID());
|
||||
$device = idx($devices, $interface->getDevicePHID());
|
||||
if (!$network || !$device) {
|
||||
$this->didRejectResult($interface);
|
||||
unset($interfaces[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$interface->attachNetwork($network);
|
||||
$interface->attachDevice($device);
|
||||
}
|
||||
|
||||
return $interfaces;
|
||||
}
|
||||
|
||||
protected function buildWhereClause($conn_r) {
|
||||
$where = array();
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->networkPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'networkPHID IN (%Ls)',
|
||||
$this->networkPHIDs);
|
||||
}
|
||||
|
||||
if ($this->devicePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'devicePHID IN (%Ls)',
|
||||
$this->devicePHIDs);
|
||||
}
|
||||
|
||||
if ($this->addresses !== null) {
|
||||
$parts = array();
|
||||
foreach ($this->addresses as $address) {
|
||||
$parts[] = qsprintf(
|
||||
$conn_r,
|
||||
'(networkPHID = %s AND address = %s AND port = %d)',
|
||||
$address->getNetworkPHID(),
|
||||
$address->getAddress(),
|
||||
$address->getPort());
|
||||
}
|
||||
$where[] = implode(' OR ', $parts);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorAlmanacApplication';
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ final class AlmanacDeviceTransaction
|
|||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_NAME = 'almanac:device:name';
|
||||
const TYPE_INTERFACE = 'almanac:device:interface';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'almanac';
|
||||
|
@ -17,6 +18,26 @@ final class AlmanacDeviceTransaction
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$phids = parent::getRequiredHandlePHIDs();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_INTERFACE:
|
||||
if ($old) {
|
||||
$phids[] = $old['networkPHID'];
|
||||
}
|
||||
if ($new) {
|
||||
$phids[] = $new['networkPHID'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
|
||||
|
@ -37,9 +58,43 @@ final class AlmanacDeviceTransaction
|
|||
$new);
|
||||
}
|
||||
break;
|
||||
case self::TYPE_INTERFACE:
|
||||
if ($old && $new) {
|
||||
return pht(
|
||||
'%s changed interface %s on this device to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->describeInterface($old),
|
||||
$this->describeInterface($new));
|
||||
} else if ($old) {
|
||||
return pht(
|
||||
'%s removed the interface %s from this device.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->describeInterface($new));
|
||||
} else if ($new) {
|
||||
return pht(
|
||||
'%s added the interface %s to this device.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->describeInterface($new));
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function shouldGenerateOldValue() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_INTERFACE:
|
||||
return false;
|
||||
}
|
||||
return parent::shouldGenerateOldValue();
|
||||
}
|
||||
|
||||
private function describeInterface(array $info) {
|
||||
return pht(
|
||||
'%s:%s (%s)',
|
||||
$info['address'],
|
||||
$info['port'],
|
||||
$this->renderHandleLink($info['networkPHID']));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
99
src/applications/almanac/storage/AlmanacInterface.php
Normal file
99
src/applications/almanac/storage/AlmanacInterface.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterface
|
||||
extends AlmanacDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
protected $devicePHID;
|
||||
protected $networkPHID;
|
||||
protected $address;
|
||||
protected $port;
|
||||
|
||||
private $device = self::ATTACHABLE;
|
||||
private $network = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewInterface() {
|
||||
return id(new AlmanacInterface());
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'address' => 'text64',
|
||||
'port' => 'uint32',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_location' => array(
|
||||
'columns' => array('networkPHID', 'address', 'port'),
|
||||
),
|
||||
'key_device' => array(
|
||||
'columns' => array('devicePHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(
|
||||
AlmanacInterfacePHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function getDevice() {
|
||||
return $this->assertAttached($this->device);
|
||||
}
|
||||
|
||||
public function attachDevice(AlmanacDevice $device) {
|
||||
$this->device = $device;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNetwork() {
|
||||
return $this->assertAttached($this->network);
|
||||
}
|
||||
|
||||
public function attachNetwork(AlmanacNetwork $device) {
|
||||
$this->device = $device;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toAddress() {
|
||||
return AlmanacAddress::newFromParts(
|
||||
$this->getNetworkPHID(),
|
||||
$this->getAddress(),
|
||||
$this->getPort());
|
||||
}
|
||||
|
||||
public function getAddressHash() {
|
||||
return $this->toAddress()->toHash();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return $this->getDevice()->getPolicy($capability);
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return $this->getDevice()->hasAutomaticCapability($capability, $viewer);
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return array(
|
||||
pht('An interface inherits the policies of the device it belongs to.'),
|
||||
pht(
|
||||
'You must be able to view the network an interface resides on to '.
|
||||
'view the interface.'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
54
src/applications/almanac/util/AlmanacAddress.php
Normal file
54
src/applications/almanac/util/AlmanacAddress.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacAddress extends Phobject {
|
||||
|
||||
private $networkPHID;
|
||||
private $address;
|
||||
private $port;
|
||||
|
||||
private function __construct() {
|
||||
// <private>
|
||||
}
|
||||
|
||||
public function getNetworkPHID() {
|
||||
return $this->networkPHID;
|
||||
}
|
||||
|
||||
public function getAddress() {
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function getPort() {
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public static function newFromDictionary(array $dictionary) {
|
||||
return self::newFromParts(
|
||||
$dictionary['networkPHID'],
|
||||
$dictionary['address'],
|
||||
$dictionary['port']);
|
||||
}
|
||||
|
||||
public static function newFromParts($network_phid, $address, $port) {
|
||||
$addr = new AlmanacAddress();
|
||||
|
||||
$addr->networkPHID = $network_phid;
|
||||
$addr->address = $address;
|
||||
$addr->port = (int)$port;
|
||||
|
||||
return $addr;
|
||||
}
|
||||
|
||||
public function toDictionary() {
|
||||
return array(
|
||||
'networkPHID' => $this->getNetworkPHID(),
|
||||
'address' => $this->getAddress(),
|
||||
'port' => $this->getPort(),
|
||||
);
|
||||
}
|
||||
|
||||
public function toHash() {
|
||||
return PhabricatorHash::digestForIndex(json_encode($this->toDictionary()));
|
||||
}
|
||||
|
||||
}
|
69
src/applications/almanac/view/AlmanacInterfaceTableView.php
Normal file
69
src/applications/almanac/view/AlmanacInterfaceTableView.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterfaceTableView extends AphrontView {
|
||||
|
||||
private $interfaces;
|
||||
private $handles;
|
||||
|
||||
public function setHandles(array $handles) {
|
||||
$this->handles = $handles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHandles() {
|
||||
return $this->handles;
|
||||
}
|
||||
|
||||
public function setInterfaces(array $interfaces) {
|
||||
$this->interfaces = $interfaces;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInterfaces() {
|
||||
return $this->interfaces;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$interfaces = $this->getInterfaces();
|
||||
$handles = $this->getHandles();
|
||||
$viewer = $this->getUser();
|
||||
|
||||
$rows = array();
|
||||
foreach ($interfaces as $interface) {
|
||||
$rows[] = array(
|
||||
$interface->getID(),
|
||||
$handles[$interface->getNetworkPHID()]->renderLink(),
|
||||
$interface->getAddress(),
|
||||
$interface->getPort(),
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => 'small grey button',
|
||||
'href' => '/almanac/interface/edit/'.$interface->getID().'/',
|
||||
),
|
||||
pht('Edit')),
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
pht('Network'),
|
||||
pht('Address'),
|
||||
pht('Port'),
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'wide',
|
||||
'',
|
||||
'',
|
||||
'action',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue