mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-22 03:29:11 +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(
|
phutil_register_library_map(array(
|
||||||
'__library_version__' => 2,
|
'__library_version__' => 2,
|
||||||
'class' => array(
|
'class' => array(
|
||||||
|
'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
|
||||||
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
|
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
|
||||||
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
|
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
|
||||||
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
|
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
|
||||||
|
@ -28,6 +29,11 @@ phutil_register_library_map(array(
|
||||||
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
|
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
|
||||||
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
||||||
'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.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',
|
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
|
||||||
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
||||||
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
||||||
|
@ -2940,6 +2946,7 @@ phutil_register_library_map(array(
|
||||||
'require_celerity_resource' => 'applications/celerity/api.php',
|
'require_celerity_resource' => 'applications/celerity/api.php',
|
||||||
),
|
),
|
||||||
'xmap' => array(
|
'xmap' => array(
|
||||||
|
'AlmanacAddress' => 'Phobject',
|
||||||
'AlmanacConduitUtil' => 'Phobject',
|
'AlmanacConduitUtil' => 'Phobject',
|
||||||
'AlmanacConsoleController' => 'AlmanacController',
|
'AlmanacConsoleController' => 'AlmanacController',
|
||||||
'AlmanacController' => 'PhabricatorController',
|
'AlmanacController' => 'PhabricatorController',
|
||||||
|
@ -2962,6 +2969,14 @@ phutil_register_library_map(array(
|
||||||
'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction',
|
'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'AlmanacDeviceViewController' => 'AlmanacDeviceController',
|
'AlmanacDeviceViewController' => 'AlmanacDeviceController',
|
||||||
|
'AlmanacInterface' => array(
|
||||||
|
'AlmanacDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
|
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
|
||||||
|
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'AlmanacInterfaceTableView' => 'AphrontView',
|
||||||
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
|
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
|
||||||
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
'AlmanacNames' => 'Phobject',
|
'AlmanacNames' => 'Phobject',
|
||||||
|
|
|
@ -44,6 +44,9 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
||||||
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
||||||
),
|
),
|
||||||
|
'interface/' => array(
|
||||||
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController',
|
||||||
|
),
|
||||||
'network/' => array(
|
'network/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
|
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
|
||||||
|
|
|
@ -35,6 +35,8 @@ final class AlmanacDeviceViewController
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->addPropertyList($property_list);
|
->addPropertyList($property_list);
|
||||||
|
|
||||||
|
$interfaces = $this->buildInterfaceList($device);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb($device->getName());
|
$crumbs->addTextCrumb($device->getName());
|
||||||
|
|
||||||
|
@ -53,6 +55,7 @@ final class AlmanacDeviceViewController
|
||||||
array(
|
array(
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$box,
|
$box,
|
||||||
|
$interfaces,
|
||||||
$xaction_view,
|
$xaction_view,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
@ -92,4 +95,48 @@ final class AlmanacDeviceViewController
|
||||||
return $actions;
|
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 = parent::getTransactionTypes();
|
||||||
|
|
||||||
$types[] = AlmanacDeviceTransaction::TYPE_NAME;
|
$types[] = AlmanacDeviceTransaction::TYPE_NAME;
|
||||||
|
$types[] = AlmanacDeviceTransaction::TYPE_INTERFACE;
|
||||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ final class AlmanacDeviceEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case AlmanacDeviceTransaction::TYPE_NAME:
|
case AlmanacDeviceTransaction::TYPE_NAME:
|
||||||
|
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +54,7 @@ final class AlmanacDeviceEditor
|
||||||
case AlmanacDeviceTransaction::TYPE_NAME:
|
case AlmanacDeviceTransaction::TYPE_NAME:
|
||||||
$object->setName($xaction->getNewValue());
|
$object->setName($xaction->getNewValue());
|
||||||
return;
|
return;
|
||||||
|
case AlmanacDeviceTransaction::TYPE_INTERFACE:
|
||||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
return;
|
return;
|
||||||
|
@ -69,6 +72,32 @@ final class AlmanacDeviceEditor
|
||||||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
return;
|
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);
|
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||||
|
@ -134,6 +163,125 @@ final class AlmanacDeviceEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
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;
|
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 {
|
extends PhabricatorApplicationTransaction {
|
||||||
|
|
||||||
const TYPE_NAME = 'almanac:device:name';
|
const TYPE_NAME = 'almanac:device:name';
|
||||||
|
const TYPE_INTERFACE = 'almanac:device:interface';
|
||||||
|
|
||||||
public function getApplicationName() {
|
public function getApplicationName() {
|
||||||
return 'almanac';
|
return 'almanac';
|
||||||
|
@ -17,6 +18,26 @@ final class AlmanacDeviceTransaction
|
||||||
return null;
|
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() {
|
public function getTitle() {
|
||||||
$author_phid = $this->getAuthorPHID();
|
$author_phid = $this->getAuthorPHID();
|
||||||
|
|
||||||
|
@ -37,9 +58,43 @@ final class AlmanacDeviceTransaction
|
||||||
$new);
|
$new);
|
||||||
}
|
}
|
||||||
break;
|
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();
|
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…
Add table
Reference in a new issue