diff --git a/src/controllers/HubController.php b/src/controllers/HubController.php
index dbfcac08..75cb607a 100644
--- a/src/controllers/HubController.php
+++ b/src/controllers/HubController.php
@@ -21,11 +21,13 @@
use hipanel\filters\EasyAccessControl;
use hipanel\helpers\ArrayHelper;
use hipanel\models\Ref;
+use hipanel\modules\server\forms\AssignSwitchesForm;
use hipanel\modules\server\forms\HubSellForm;
use hipanel\modules\server\models\HardwareSettings;
use hiqdev\hiart\Collection;
use Yii;
use yii\base\Event;
+use yii\web\NotFoundHttpException;
class HubController extends CrudController
{
@@ -143,6 +145,41 @@ public function actions()
}
},
],
+ 'assign-switches' => [
+ 'class' => SmartUpdateAction::class,
+ 'success' => Yii::t('hipanel:server:hub', 'Switches have been edited'),
+ 'view' => 'assign-switches',
+ 'on beforeFetch' => function (Event $event) {
+ /** @var \hipanel\actions\SearchAction $action */
+ $action = $event->sender;
+ $dataProvider = $action->getDataProvider();
+ $dataProvider->query->withBindings()->select(['*']);
+ },
+ 'collection' => [
+ 'class' => Collection::class,
+ 'model' => new AssignSwitchesForm(),
+ 'scenario' => 'default',
+ ],
+ 'data' => function (Action $action, array $data) {
+ $result = [];
+ foreach ($data['models'] as $model) {
+ $result['models'][] = AssignSwitchesForm::fromOriginalModel($model);
+ }
+ if (!$result['models']) {
+ throw new NotFoundHttpException('There are no entries available for the selected operation.');
+ }
+ $result['model'] = reset($result['models']);
+
+ return $result;
+ },
+ ],
+ 'validate-switches-form' => [
+ 'class' => ValidateFormAction::class,
+ 'collection' => [
+ 'class' => Collection::class,
+ 'model' => new AssignSwitchesForm(),
+ ],
+ ],
'validate-sell-form' => [
'class' => ValidateFormAction::class,
'collection' => [
diff --git a/src/controllers/ServerController.php b/src/controllers/ServerController.php
index b71e2d2c..4971461f 100644
--- a/src/controllers/ServerController.php
+++ b/src/controllers/ServerController.php
@@ -202,7 +202,7 @@ public function actions()
],
'assign-hubs' => [
'class' => SmartUpdateAction::class,
- 'success' => Yii::t('hipanel:server', 'Hubs were assigned'),
+ 'success' => Yii::t('hipanel:server', 'Hubs have been assigned'),
'view' => 'assignHubs',
'on beforeFetch' => function (Event $event) {
/** @var \hipanel\actions\SearchAction $action */
@@ -218,7 +218,7 @@ public function actions()
'data' => function (Action $action, array $data) {
$result = [];
foreach ($data['models'] as $model) {
- $result['models'][] = AssignHubsForm::fromServer($model);
+ $result['models'][] = AssignHubsForm::fromOriginalModel($model);
}
if (!$result['models']) {
throw new NotFoundHttpException('There are no entries available for the selected operation. The type of selected records may not be suitable for the selected operation.');
diff --git a/src/forms/AssignHubsForm.php b/src/forms/AssignHubsForm.php
index 6fdecf4e..8aaf38d3 100644
--- a/src/forms/AssignHubsForm.php
+++ b/src/forms/AssignHubsForm.php
@@ -2,7 +2,7 @@
namespace hipanel\modules\server\forms;
-use hipanel\modules\server\models\Binding;
+use hipanel\base\ModelTrait;
use hipanel\modules\server\models\Server;
use Yii;
@@ -11,35 +11,19 @@
*/
class AssignHubsForm extends Server
{
- use \hipanel\base\ModelTrait;
+ use ModelTrait;
/**
- * @inheritdoc
+ * @var array
*/
- public static function tableName()
- {
- return 'server';
- }
+ public $switchVariants = ['net', 'kvm', 'pdu', 'rack', 'pdu2', 'nic2', 'ipmi'];
/**
- * Create AttachHubsForm model from Server model
- *
- * @param Server $server
- * @return AssignHubsForm
+ * @inheritdoc
*/
- public static function fromServer(Server $server): AssignHubsForm
+ public static function tableName()
{
- $attributes = array_merge($server->getAttributes(), []);
- $model = new self(['scenario' => 'default']);
- foreach ($server->bindings as $binding) {
- if ($model->hasAttribute($binding->typeWithNo . '_id')) {
- $attributes[$binding->typeWithNo . '_id'] = $binding->switch_id;
- $attributes[$binding->typeWithNo . '_port'] = $binding->port;
- }
- }
- $model->setAttributes($attributes);
-
- return $model;
+ return 'server';
}
/**
@@ -47,11 +31,7 @@ public static function fromServer(Server $server): AssignHubsForm
*/
public function rules()
{
- return array_merge(parent::rules(), [
- [['id'], 'required'],
- [['net_id', 'kvm_id', 'pdu_id', 'rack_id', 'pdu2_id', 'nic2_id', 'ipmi_id'], 'integer'],
- [['net_port', 'kvm_port', 'pdu_port', 'rack_port', 'pdu2_port', 'nic2_port', 'ipmi_port'], 'string'],
- ], $this->generateUniqueValidators());
+ return array_merge(parent::rules(), $this->defaultSwitchRules(), $this->generateUniqueValidators());
}
public function attributeLabels()
@@ -61,54 +41,5 @@ public function attributeLabels()
'nic2' => Yii::t('hipanel:server', 'Switch 2'),
]);
}
-
- /**
- * For compatibility with [[hiqdev\hiart\Collection]]
- *
- * @param $defaultScenario
- * @param array $data
- * @param array $options
- * @return mixed
- */
- public function batchQuery($defaultScenario, $data = [], array $options = [])
- {
- $map = [
- 'update' => 'assign-hubs',
- ];
- $scenario = isset($map[$defaultScenario]) ? $map[$defaultScenario] : $defaultScenario;
-
- return (new Server)->batchQuery($scenario, $data, $options);
- }
-
- private function generateUniqueValidators(): array
- {
- $rules = [];
-
- foreach (['net', 'kmv', 'pdu', 'rack', 'pdu2', 'nic2', 'ipmi'] as $variant) {
- $rules[] = [
- [$variant . '_port'],
- function ($attribute, $params, $validator) use ($variant) {
- if ($this->{$attribute} && $this->{$variant . '_id'}) {
- $query = Binding::find();
- $query->andWhere(['port' => $this->{$attribute}]);
- $query->andWhere(['switch_id' => $this->{$variant . '_id'}]);
- $query->andWhere(['ne', 'base_device_id', $this->id]);
- /** @var Binding[] $bindings */
- $bindings = $query->all();
- if (!empty($bindings)) {
- $binding = reset($bindings);
- $this->addError($attribute, Yii::t('hipanel:server', '{switch}::{port} already taken by {device}', [
- 'switch' => $binding->switch_name,
- 'port' => $binding->port,
- 'device' => $binding->device_name,
- ]));
- }
- }
- },
- ];
- }
-
- return $rules;
- }
}
diff --git a/src/forms/AssignSwitchesForm.php b/src/forms/AssignSwitchesForm.php
new file mode 100644
index 00000000..ff4b020d
--- /dev/null
+++ b/src/forms/AssignSwitchesForm.php
@@ -0,0 +1,30 @@
+defaultSwitchRules(), $this->generateUniqueValidators());
+ }
+}
diff --git a/src/menus/HubDetailMenu.php b/src/menus/HubDetailMenu.php
index 8371b515..fdb5ae9a 100644
--- a/src/menus/HubDetailMenu.php
+++ b/src/menus/HubDetailMenu.php
@@ -10,6 +10,8 @@
namespace hipanel\modules\server\menus;
+use Yii;
+
class HubDetailMenu extends \hipanel\menus\AbstractDetailMenu
{
public $model;
@@ -17,7 +19,16 @@ class HubDetailMenu extends \hipanel\menus\AbstractDetailMenu
public function items()
{
$actions = HubActionsMenu::create(['model' => $this->model])->items();
- $items = array_merge($actions, []);
+ $items = array_merge($actions, [
+ 'assign-switches' => [
+ 'label' => Yii::t('hipanel:server', 'Switches'),
+ 'icon' => 'fa-plug',
+ 'url' => ['@hub/assign-switches', 'id' => $this->model->id],
+ 'linkOptions' => [
+ 'data-pjax' => 0,
+ ],
+ ],
+ ]);
unset($items['view']);
return $items;
diff --git a/src/menus/ServerActionsMenu.php b/src/menus/ServerActionsMenu.php
index efa5e4ed..a435c5eb 100644
--- a/src/menus/ServerActionsMenu.php
+++ b/src/menus/ServerActionsMenu.php
@@ -52,7 +52,7 @@ public function items(): array
],
'assign-hubs' => [
'label' => Yii::t('hipanel:server', 'Assign hubs'),
- 'icon' => 'fa-exchange',
+ 'icon' => 'fa-plug',
'url' => ['@server/assign-hubs', 'id' => $this->model->id],
'visible' => Yii::$app->user->can('server.update'),
'linkOptions' => [
diff --git a/src/messages/ru/hipanel:server.php b/src/messages/ru/hipanel:server.php
index 66f21725..597296b3 100644
--- a/src/messages/ru/hipanel:server.php
+++ b/src/messages/ru/hipanel:server.php
@@ -308,4 +308,5 @@
'Mail settings have been changed successfully' => 'Настройки почты были успешно изменены',
'Number of mailboxes' => 'Количество почтовых ящиков',
'View parts' => 'Смотреть детали',
+ 'Hubs have been assigned' => 'Свитчи назначены',
];
diff --git a/src/models/AssignSwitchInterface.php b/src/models/AssignSwitchInterface.php
new file mode 100644
index 00000000..ed2fbf87
--- /dev/null
+++ b/src/models/AssignSwitchInterface.php
@@ -0,0 +1,8 @@
+hasOne(HardwareSettings::class, ['id' => 'id']);
}
+
+ /**
+ * {@inheritdoc}
+ * @return HubQuery
+ */
+ public static function find($options = [])
+ {
+ return new HubQuery(get_called_class(), [
+ 'options' => $options,
+ ]);
+ }
}
diff --git a/src/models/Server.php b/src/models/Server.php
index 0ce97a64..64cc50cb 100644
--- a/src/models/Server.php
+++ b/src/models/Server.php
@@ -10,11 +10,14 @@
namespace hipanel\modules\server\models;
+use hipanel\base\Model;
+use hipanel\base\ModelTrait;
use hipanel\models\Ref;
use hipanel\modules\finance\models\Sale;
use hipanel\modules\hosting\models\Ip;
use hipanel\modules\server\helpers\ServerHelper;
use hipanel\modules\server\models\query\ServerQuery;
+use hipanel\modules\server\models\traits\AssignSwitchTrait;
use hipanel\validators\EidValidator;
use hipanel\validators\RefValidator;
use Yii;
@@ -28,9 +31,9 @@
*
* @property-read HardwareSale[] $hardwareSales
*/
-class Server extends \hipanel\base\Model
+class Server extends Model implements AssignSwitchInterface
{
- use \hipanel\base\ModelTrait;
+ use ModelTrait, AssignSwitchTrait;
const STATE_OK = 'ok';
const STATE_DISABLED = 'disabled';
diff --git a/src/models/query/HubQuery.php b/src/models/query/HubQuery.php
new file mode 100644
index 00000000..ead95443
--- /dev/null
+++ b/src/models/query/HubQuery.php
@@ -0,0 +1,27 @@
+user->can('hub.read')) {
+ $this->joinWith('bindings');
+ $this->andWhere(['with_bindings' => true]);
+ }
+
+ return $this;
+ }
+}
diff --git a/src/models/traits/AssignSwitchTrait.php b/src/models/traits/AssignSwitchTrait.php
new file mode 100644
index 00000000..6c752844
--- /dev/null
+++ b/src/models/traits/AssignSwitchTrait.php
@@ -0,0 +1,115 @@
+getAttributes(), []);
+ $model = new static(['scenario' => 'default']);
+ foreach ($originalModel->bindings as $binding) {
+ $attribute = $binding->typeWithNo . '_id';
+ if ($model->hasAttribute($attribute)) {
+ $attributes[$binding->typeWithNo . '_id'] = $binding->switch_id;
+ $attributes[$binding->typeWithNo . '_port'] = $binding->port;
+ }
+ }
+ $model->setAttributes($attributes);
+
+ return $model;
+ }
+
+ public function defaultSwitchRules(): array
+ {
+ $variantIds = [];
+ $variantPorts = [];
+ foreach ($this->switchVariants as $variant) {
+ $variantIds[] = $variant . '_id';
+ $variantPorts[] = $variant . '_port';
+ }
+ return [
+ [['id'], 'required'],
+ [$variantIds, 'integer'],
+ [$variantPorts, 'string'],
+ ];
+ }
+
+ /**
+ * For compatibility with [[hiqdev\hiart\Collection]]
+ *
+ * @param $defaultScenario
+ * @param array $data
+ * @param array $options
+ *
+ * @return mixed
+ */
+ public function batchQuery($defaultScenario, $data = [], array $options = [])
+ {
+ $map = [
+ 'update' => 'assign-hubs',
+ ];
+ $scenario = isset($map[$defaultScenario]) ? $map[$defaultScenario] : $defaultScenario;
+
+ return parent::batchQuery($scenario, $data, $options);
+ }
+
+ /**
+ * Added to model's rules list of switch pairs
+ *
+ * @return array
+ * @throws InvalidConfigException
+ */
+ protected function generateUniqueValidators(): array
+ {
+ if (empty($this->switchVariants)) {
+ throw new InvalidConfigException('Please specify `switchVariants` array to use AssignSwitchTrait::generateUniqueValidators()');
+ }
+ $rules = [];
+
+ foreach ($this->switchVariants as $variant) {
+ $rules[] = [
+ [$variant . '_port'],
+ function ($attribute, $params, $validator) use ($variant) {
+ if ($this->{$attribute} && $this->{$variant . '_id'}) {
+ $query = Binding::find();
+ $query->andWhere(['port' => $this->{$attribute}]);
+ $query->andWhere(['switch_id' => $this->{$variant . '_id'}]);
+ $query->andWhere(['ne', 'base_device_id', $this->id]);
+ /** @var Binding[] $bindings */
+ $bindings = $query->all();
+ if (!empty($bindings)) {
+ $binding = reset($bindings);
+ $this->addError($attribute, Yii::t('hipanel:server', '{switch}::{port} already taken by {device}', [
+ 'switch' => $binding->switch_name,
+ 'port' => $binding->port,
+ 'device' => $binding->device_name,
+ ]));
+ }
+ }
+ },
+ ];
+ }
+
+ return $rules;
+ }
+}
diff --git a/src/views/hub/assign-switches.php b/src/views/hub/assign-switches.php
new file mode 100644
index 00000000..d83de9f0
--- /dev/null
+++ b/src/views/hub/assign-switches.php
@@ -0,0 +1,28 @@
+title = Yii::t('hipanel:server:hub', 'Edit switches');
+$this->params['breadcrumbs'][] = ['label' => Yii::t('hipanel:server', 'Switches'), 'url' => ['index']];
+if (count($models) === 1) {
+ $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
+}
+$this->params['breadcrumbs'][] = $this->title;
+
+?>
+
+ 'assign-switches-form',
+ 'enableClientValidation' => true,
+ 'validateOnBlur' => true,
+ 'enableAjaxValidation' => true,
+ 'validationUrl' => Url::toRoute(['validate-switches-form', 'scenario' => 'default']),
+]) ?>
+
+= AssignSwitchesPage::widget([
+ 'models' => $models,
+ 'switchVariants' => ['net', 'kvm', 'pdu', 'rack', 'console'],
+ 'form' => $form,
+]) ?>
diff --git a/src/views/hub/index.php b/src/views/hub/index.php
index dc04bea2..07e5a18c 100644
--- a/src/views/hub/index.php
+++ b/src/views/hub/index.php
@@ -69,6 +69,7 @@
user->can('hub.update')) : ?>
= $page->renderBulkButton('update', ' ' . Yii::t('hipanel', 'Update'))?>
+ = $page->renderBulkButton('assign-switches', ' ' . Yii::t('hipanel:server:hub', 'Switches')) ?>
endContent('bulk-actions') ?>
diff --git a/src/views/server/assignHubs.php b/src/views/server/assignHubs.php
index 1b8f3478..59813459 100644
--- a/src/views/server/assignHubs.php
+++ b/src/views/server/assignHubs.php
@@ -1,6 +1,7 @@
params['breadcrumbs'][] = ['label' => reset($models)->name, 'url' => ['view', 'id' => reset($models)->id]];
}
$this->params['breadcrumbs'][] = $this->title;
-$variantMap = [
- 'pdu2' => 'pdu',
- 'ipmi' => 'net',
- 'nic2' => 'net',
-];
?>
Url::toRoute(['validate-assign-hubs-form', 'scenario' => 'default']),
]) ?>
- 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
- 'widgetBody' => '.container-items', // required: css class selector
- 'widgetItem' => '.item', // required: css class
- 'limit' => 99, // the maximum times, an element can be cloned (default 999)
- 'min' => 1, // 0 or 1 (default 1)
- 'insertButton' => '.add-item', // css class
- 'deleteButton' => '.remove-item', // css class
- 'model' => reset($models),
- 'formId' => 'assign-hubs-form',
- 'formFields' => [
- 'id',
- 'rack_id',
- 'rack_port',
- 'net_id',
- 'net_port',
- 'pdu_id',
- 'pdu_port',
- 'ipmi_id',
- 'ipmi_port',
- 'kvm_id',
- 'kvm_port',
- 'nic2_id',
- 'nic2_port',
- 'pdu2_port',
- 'pdu2_port',
- ],
+= AssignSwitchesPage::widget([
+ 'models' => $models,
+ 'switchVariants' => ['rack', 'net', 'pdu', 'ipmi', 'kvm', 'nic2', 'pdu2'],
+ 'form' => $form,
]) ?>
-
- $model) : ?>
-
- = Html::activeHiddenInput($model, "[$i]id") ?>
-
-
-
-
-
-
-
-
-
-
- = Html::label($model->getAttributeLabel($variant)) ?> |
-
-
-
-
-
- = $form->field($model, "[$i]{$variant}_id")->widget(HubCombo::class, [
- 'name' => $variant,
- 'hubType' => $variantMap[$variant] ?? $variant,
- ])->label(false) ?>
- |
-
- = $form->field($model, "[$i]{$variant}_port")->label(false) ?>
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- = Html::submitButton(Yii::t('hipanel', 'Save'), ['class' => 'btn btn-success']) ?>
-
- = Html::button(Yii::t('hipanel', 'Cancel'), ['class' => 'btn btn-default', 'onclick' => 'history.go(-1)']) ?>
-
-
-
-
diff --git a/src/views/server/index.php b/src/views/server/index.php
index 9ea45b03..e1865216 100644
--- a/src/views/server/index.php
+++ b/src/views/server/index.php
@@ -101,7 +101,7 @@
'options' => ['class' => 'pull-right'],
'items' => array_filter([
[
- 'label' => ' ' . Yii::t('hipanel:server', 'Assign hubs'),
+ 'label' => ' ' . Yii::t('hipanel:server', 'Assign hubs'),
'url' => '#',
'linkOptions' => ['data-action' => 'assign-hubs'],
'visible' => Yii::$app->user->can('server.update'),
diff --git a/src/widgets/AssignSwitchesPage.php b/src/widgets/AssignSwitchesPage.php
new file mode 100644
index 00000000..d39d8d76
--- /dev/null
+++ b/src/widgets/AssignSwitchesPage.php
@@ -0,0 +1,50 @@
+ 'pdu',
+ 'ipmi' => 'net',
+ 'nic2' => 'net',
+ ];
+
+ public function run()
+ {
+ return $this->render('AssignSwitchesPage', [
+ 'switchVariants' => $this->switchVariants,
+ 'form' => $this->form,
+ 'models' => $this->models,
+ ]);
+ }
+
+ public function getFormFields(): array
+ {
+ $fields = [];
+ foreach ($this->switchVariants as $name) {
+ $fields[] = $name . '_id';
+ $fields[] = $name . '_port';
+ }
+
+ return array_merge(['id'], $fields);
+ }
+}
diff --git a/src/widgets/views/AssignSwitchesPage.php b/src/widgets/views/AssignSwitchesPage.php
new file mode 100644
index 00000000..c2760a6b
--- /dev/null
+++ b/src/widgets/views/AssignSwitchesPage.php
@@ -0,0 +1,74 @@
+
+
+ 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
+ 'widgetBody' => '.container-items', // required: css class selector
+ 'widgetItem' => '.item', // required: css class
+ 'limit' => 99, // the maximum times, an element can be cloned (default 999)
+ 'min' => 1, // 0 or 1 (default 1)
+ 'insertButton' => '.add-item', // css class
+ 'deleteButton' => '.remove-item', // css class
+ 'model' => reset($models),
+ 'formId' => Inflector::camel2id(reset($models)->formName()) . '-form',
+ 'formFields' => $this->context->getFormFields(),
+]) ?>
+
+ $model) : ?>
+
+ = Html::activeHiddenInput($model, "[$i]id") ?>
+
+
+
+
+
+
+
+
+
+
+ = Html::label($model->getAttributeLabel($variant)) ?> |
+
+
+
+
+
+ = $form->field($model, "[$i]{$variant}_id")->widget(HubCombo::class, [
+ 'name' => $variant,
+ 'hubType' => $this->context->variantMap[$variant] ?? $variant,
+ ])->label(false) ?>
+ |
+
+ = $form->field($model, "[$i]{$variant}_port")->label(false) ?>
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = Html::submitButton(Yii::t('hipanel', 'Save'), ['class' => 'btn btn-success']) ?>
+
+ = Html::button(Yii::t('hipanel', 'Cancel'), ['class' => 'btn btn-default', 'onclick' => 'history.go(-1)']) ?>
+
+
+
\ No newline at end of file