VmUpdateProvider.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <?php
  2. namespace ModulesGarden\Servers\ProxmoxCloudVps\App\UI\VmUpdate\Providers;
  3. use Illuminate\Database\Capsule\Manager as DB;
  4. use MGProvision\Proxmox\v2\Api;
  5. use MGProvision\Proxmox\v2\models\Kvm;
  6. use MGProvision\Proxmox\v2\models\MountPoint;
  7. use MGProvision\Proxmox\v2\models\NetworkDeviceKvm;
  8. use MGProvision\Proxmox\v2\repository\FileRepository;
  9. use MGProvision\Proxmox\v2\repository\StorageRepository;
  10. use ModulesGarden\ProxmoxAddon\App\Events\Cloud\LxcUpdateEvent;
  11. use ModulesGarden\ProxmoxAddon\App\Events\Cloud\QemuUpdateEvent;
  12. use ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\Agent\ChangePasswordJob;
  13. use ModulesGarden\ProxmoxAddon\App\Jobs\Cloud\Agent\ConfigureNetworkJob;
  14. use ModulesGarden\ProxmoxAddon\App\Models\VirtualInterface;
  15. use ModulesGarden\ProxmoxAddon\App\Models\VmIpAddress;
  16. use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
  17. use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
  18. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AdditionalDiskService;
  19. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AdditionalMountPointService;
  20. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\AgentService;
  21. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\IpSetIpFilterService;
  22. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\NetworkService;
  23. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\ProductService;
  24. use ModulesGarden\ProxmoxAddon\App\Services\Cloud\VirtualInterfaceConverter;
  25. use ModulesGarden\Servers\ProxmoxCloudVps\App\UI\VmCreate\Providers\VmCreateProvider;
  26. use ModulesGarden\Servers\ProxmoxCloudVps\Core\UI\ResponseTemplates\HtmlDataJsonResponse;
  27. use function ModulesGarden\ProxmoxAddon\Core\Helper\fire;
  28. use function ModulesGarden\ProxmoxAddon\Core\Helper\queue;
  29. use function ModulesGarden\Servers\ProxmoxCloudVps\Core\Helper\sl;
  30. class VmUpdateProvider extends VmCreateProvider
  31. {
  32. use ApiService;
  33. use ProductService;
  34. /**
  35. * VmUpdateProvider constructor.
  36. */
  37. public function __construct()
  38. {
  39. $this->model = VmModel::class;
  40. }
  41. public function read()
  42. {
  43. $this->vmModel = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
  44. $this->data = $this->vmModel->toArray();
  45. $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
  46. $config = $vm->config();
  47. $this->data['password'] = $this->vmModel->getPassword();
  48. $this->data['ciuser'] = $config['ciuser'];
  49. $this->data['cipassword'] = $config['cipassword'];
  50. $this->data['description'] = html_entity_decode($config['description']);
  51. $this->data['searchdomain'] = $config['searchdomain'];
  52. $ns = explode(" ",$config['nameserver']);
  53. $this->data['nameserver[0]'] = $ns[0];
  54. $this->data['nameserver[1]'] = $ns[1];
  55. if(!$this->configuration()->isOrderPublicIp()){
  56. $this->data['ipv4'] = $this->vmModel->ipv4Addresses->count();
  57. $this->data['ipv6'] = $this->vmModel->ipv6Addresses->count();
  58. }
  59. //Boot order
  60. if($vm instanceof Kvm){
  61. $bootOrder = $vm->getBootOrder();
  62. $this->data['bootOrder0'] = $bootOrder[0];
  63. $this->data['bootOrder1'] = $bootOrder[1];
  64. $this->data['bootOrder2'] = $bootOrder[2];
  65. $options = [
  66. 0 => sl("lang")->tr("None"),
  67. "c" => sl("lang")->tr("Disk"),
  68. "d" => sl("lang")->tr("CD-ROM"),
  69. "n" => sl("lang")->tr("Network"),
  70. ];
  71. if(version_compare($this->api()->getVersion(), "6.3", '>=')
  72. && !in_array("c", (array) $bootOrder )){
  73. $cdrom = $vm->cdrom();
  74. $nd = $vm->getNetworkDevices();
  75. $options = [
  76. 0 => sl("lang")->tr("None"),
  77. $vm->getMasterHardDisk()->getId() => sl("lang")->tr("Disk"),
  78. ];
  79. if($cdrom['bus']){
  80. $options[$cdrom['bus']] = sl("lang")->tr("CD-ROM");
  81. }
  82. if($nd[0] instanceof NetworkDeviceKvm){
  83. $options[$nd[0]->getId()] = sl("lang")->tr("Network");
  84. }
  85. }
  86. $this->availableValues['bootOrder0'] = $options;
  87. $this->availableValues['bootOrder1'] = $options;
  88. $this->availableValues['bootOrder2'] = $options;
  89. }
  90. //sshkeys
  91. $this->data['sshkeys'] = rawurldecode($vm->config()['sshkeys']);
  92. //cpu Priority
  93. if($this->configuration()->hasCpuPriority()){
  94. for($i=1; $i<=5; $i++){
  95. $cpuunits = $this->configuration()->get('cpuunitsPriority'.$i);
  96. $cpulimit = $this->configuration()->get('cpulimitPriority'.$i);
  97. if($this->vmModel->cpulimit >= $cpulimit){
  98. $this->data['cpuPriority'] = $i;
  99. }
  100. if($this->vmModel->cpuunits >= $cpuunits){
  101. $this->data['cpuPriority'] = $i;
  102. }
  103. }
  104. }
  105. //iso
  106. if ($this->configuration()->isQemu() && $this->configuration()->isPermissionIsoImage()) {
  107. $this->availableValues['iso'] = ["none" => sl('lang')->abtr("None")];
  108. $storageRepository = new StorageRepository();
  109. $storageRepository->findByNodes([$vm->getNode()])
  110. ->findEnabed();
  111. $storages = $storageRepository->fetchAsArray();
  112. $isoRepository = new FileRepository();
  113. $isoRepository->findByNodes([$vm->getNode()])
  114. ->findByStorages($storages);
  115. $isoRepository->findIso();
  116. foreach ($isoRepository->fetch() as $entity) {
  117. if ($this->configuration()->isPermissionIsoImages() && !in_array($entity->getVolid(), $this->configuration()->getPermissionIsoImages())) {
  118. continue;
  119. }
  120. $this->availableValues['iso'][$entity->getVolid()] = $entity->getFriendlyName();
  121. }
  122. if( $vm->getCdRomRepository()->first()){
  123. $this->data['iso']= $vm->getCdRomRepository()->first()->getLocation();
  124. }
  125. }
  126. }
  127. public function update()
  128. {
  129. $this->vmModel = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
  130. $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
  131. $this->networkService = new NetworkService();
  132. if(!$this->configuration()->isOrderPublicIp()){
  133. $ipv4 = (int) $this->getFormDataValues()['ipv4'] - (int) $this->vmModel->ipv4Addresses->count();
  134. $ipv6 = (int)$this->getFormDataValues()['ipv6'] - (int) $this->vmModel->ipv6Addresses->count();
  135. $this->networkService->hasIp($ipv4, $ipv6, $this->vmModel->node, $this->configuration()->getBridge());
  136. $this->networkService->addIp($ipv4, $ipv6, $this->vmModel->node, $this->configuration()->getBridge());
  137. $this->networkService->unassignIpAddressesAndDeleteNetwork($ipv4, $ipv6);
  138. foreach (VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
  139. ->ofVmId($this->vmModel->id)->ofNetNull()->get() as $vmIp){
  140. $vi = new VirtualInterface();
  141. $vi->ip = $vmIp->ip;
  142. $vi->ip_long = ip2long($vmIp->ip);
  143. $vi->vm_id = $this->vmModel->id;
  144. $vi->vn_id = 0;
  145. $vi->hosting_id = $this->getWhmcsParamByKey('serviceid');
  146. $vi->save();
  147. }
  148. }
  149. //load data
  150. $this->getFormDataValues();
  151. if ($vm instanceof Kvm)
  152. {
  153. //iso
  154. if ($vm->cdrom())
  155. {
  156. $vm->updateCdrom($this->formData['iso']);
  157. }
  158. //Boot order
  159. $bootOrder = null;
  160. $order = [];
  161. for ($i = 0; $i <= 2; $i++)
  162. {
  163. if ($this->formData['bootOrder' . $i])
  164. {
  165. $bootOrder .= $this->formData['bootOrder' . $i];
  166. if(!in_array($this->formData['bootOrder' . $i],["c","d","n"])){
  167. $order[] = $this->formData['bootOrder' . $i];
  168. }
  169. }
  170. }
  171. if(version_compare($this->api()->getVersion(), "6.3", '>=') && !empty($order)){
  172. //order=scsi0;ide0;ide1;net0
  173. $vm->updateConfig(['boot' => "order=".implode(";", $order)]);
  174. }else{
  175. $vm->changeBootOrder($bootOrder);
  176. }
  177. //sshkeys
  178. if($this->configuration()->isPermissionSshkeys()){
  179. if ($this->formData['sshkeys'])
  180. {
  181. $vm->updateConfig(["sshkeys" => rawurlencode($this->formData['sshkeys'])]);
  182. } else if($vm->config()['sshkeys'])
  183. {
  184. $vm->deleteConfig('sshkeys');
  185. }
  186. }
  187. }
  188. try{
  189. Api::beginTransaction();
  190. DB::beginTransaction();
  191. $this->fillVmModel();
  192. logModuleCall(
  193. 'proxmoxCloud',
  194. __FUNCTION__,
  195. $this->getFormDataValues(),
  196. 'Debug',
  197. $this->formData
  198. );
  199. if($this->getFormDataValues()['password']){
  200. $this->vmModel->setPassword($this->getFormDataValues()['password']);
  201. }
  202. if($this->getFormDataValues()['ciuser']){
  203. $vm->updateConfig(["ciuser" => $this->formData['ciuser']]);
  204. }
  205. if($this->getFormDataValues()['cipassword']){
  206. $vm->updateConfig(["cipassword" => $this->formData['cipassword']]);
  207. $this->vmModel->setPassword($this->getFormDataValues()['cipassword']);
  208. }
  209. if($this->configuration()->isCalculateSocketsAndCores()){
  210. $this->calculateSocketsAndCores();
  211. }
  212. $this->vmModel->save();
  213. if($this->configuration()->isQemu()){
  214. fire(new QemuUpdateEvent($this->vmModel, $this->formData));
  215. }
  216. if($this->configuration()->isLxc()){
  217. fire(new LxcUpdateEvent($this->vmModel, $this->formData));
  218. }
  219. if ($this->configuration()->isDetailsCombinedView()) {
  220. /* disabled by Thurdata is case of redisign of the combined view
  221. //virtual interfaces
  222. $this->virtualNetworkUpdate();
  223. //IP Set
  224. if ($this->configuration()->isIpsetIpFilter()){
  225. $ipSetFilterService = new IpSetIpFilterService();
  226. $ipSetFilterService->create();
  227. }
  228. //disks
  229. $this->disksUpdate();
  230. */
  231. }
  232. $agentEnabled = (new AgentService())->isEnabled();
  233. if($agentEnabled && $this->configuration()->isAgentConfigureNetwork()){
  234. queue(ConfigureNetworkJob::class,
  235. ['hostingId' => $this->getWhmcsParamByKey('serviceid')],
  236. null,
  237. "hosting",
  238. $this->getWhmcsParamByKey("serviceid"),
  239. $this->vmModel->id
  240. );
  241. }
  242. if($agentEnabled && $this->configuration()->isAgentPassword()){
  243. queue(ChangePasswordJob::class,
  244. ['hostingId' => $this->getWhmcsParamByKey('serviceid')],
  245. null,
  246. "hosting",
  247. $this->getWhmcsParamByKey("serviceid"),
  248. $this->vmModel->id
  249. );
  250. }
  251. DB::commit();
  252. return (new HtmlDataJsonResponse())
  253. ->setMessageAndTranslate('Server has been updated')
  254. ->setCallBackFunction('pcVmUpdatedAjaxDone')
  255. ->addRefreshTargetId('serviceInformationDataTable')
  256. ->setCallBackFunction('mgLocationReload');
  257. }catch (\Exception $ex){
  258. DB::rollBack();
  259. Api::commit();
  260. return (new HtmlDataJsonResponse())
  261. ->setStatusError()
  262. ->setMessageAndTranslate($ex->getMessage());
  263. }
  264. }
  265. private function virtualNetworkUpdate()
  266. {
  267. try {
  268. $viIds = [];
  269. //delete
  270. for ($i = 0; $i <= 20; $i++) {
  271. if ($this->formData['virtualInterface' . $i]) {
  272. $viIds[] = $this->formData['virtualInterface' . $i]['id'];
  273. }
  274. }
  275. $virtualInterfaces = VirtualInterface::ofHostingId($this->getWhmcsParamByKey('serviceid'))
  276. ->ofVmId($this->vmModel->id);
  277. if ($viIds) {
  278. $virtualInterfaces->notId($viIds);
  279. }
  280. $this->networkService->deleteByIpAddresses($virtualInterfaces->get());
  281. DB::commit();
  282. DB::beginTransaction();
  283. //create
  284. $newVi = [];
  285. for ($i = 1; $i <= 20; $i++) {
  286. if (!$this->formData['virtualNetwork' . $i]) {
  287. continue;
  288. }
  289. $ip = $this->formData['virtualNetwork' . $i]['ip'];
  290. $virtualNetworkId = $this->formData['virtualNetwork' . $i]['id'];
  291. //lock public ip
  292. if ($virtualNetworkId == 'public') {
  293. VmIpAddress::ofHostingId($this->getWhmcsParamByKey('serviceid'))
  294. ->ofIp($ip)
  295. ->update(['vm_id' => $this->vmModel->id]);
  296. }
  297. $vi = new VirtualInterface();
  298. $vi->ip = $ip;
  299. $vi->ip_long = ip2long($ip);
  300. $vi->vm_id = $this->vmModel->id;
  301. $vi->vn_id = $virtualNetworkId == "public" ? 0 : $virtualNetworkId;
  302. $vi->hosting_id = $this->getWhmcsParamByKey('serviceid');
  303. $vi->save();
  304. $newVi[] = $vi;
  305. $viIds[] = $vi->id;
  306. }
  307. if ($newVi) {
  308. $config = (new VirtualInterfaceConverter($newVi, \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm()))->asConfig();
  309. if(!empty($config)){
  310. \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm()->updateConfig($config);
  311. }
  312. }
  313. DB::commit();
  314. } catch (\Exception $ex) {
  315. DB::rollBack();
  316. Api::commit();
  317. throw $ex;
  318. }
  319. }
  320. private function disksUpdate()
  321. {
  322. $vm = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVm();
  323. $vmModel = \ModulesGarden\ProxmoxAddon\Core\Helper\sl('Vm')->getVmModel();
  324. $additonalDiskService = $this->configuration()->isQemu() ? new AdditionalDiskService() : new AdditionalMountPointService();
  325. //Update
  326. $diskIds=[];
  327. $resizeTask = null;
  328. for ($i = 0; $i <= 20; $i++) {
  329. if ($this->formData['disks' . $i]) {
  330. $id = $this->formData['disks' . $i]['id'];
  331. $size = $this->formData['disks' . $i]['size'];
  332. $backup = $this->formData['disks' . $i]['backup'] == "on" ? null : 0;
  333. $mp = $this->formData['disks' . $i]['mp'];
  334. $hdd = $vm->getHardDiskRepostiory()->findById($id);
  335. if($resizeTask && $this->configuration()->isLxc()){
  336. for($i=0; $i<=15; $i++){
  337. if($vm->node()->task($resizeTask)->isDone()){
  338. $resizeTask = null;
  339. break;
  340. }
  341. sleep(1);
  342. }
  343. }
  344. //Backup
  345. $hdd->setBackup($backup);
  346. //mp
  347. if($hdd instanceof MountPoint){
  348. $hdd->setBackup($this->formData['disks' . $i]['backup'] == "on" ? 1 : null);
  349. $hdd->setMp($mp);
  350. }
  351. $hdd->update();
  352. //resize
  353. if ((int)$hdd->getGb() < (int)$size)
  354. {
  355. $size = "+" . abs((int)$size - (int)$hdd->getGb()) . "G";
  356. $resizeTask = $hdd->resize($size);
  357. }
  358. $diskIds[] = $id;
  359. }
  360. }
  361. //delete
  362. foreach ($vm->getHardDiskRepostiory()->fetch() as $disk){
  363. if(!$disk->isMaster() && !in_array($disk->getId() ,$diskIds)){
  364. $disk->delete();
  365. }
  366. }
  367. $vm->getHardDiskRepostiory()->deleteUnused();
  368. //Create
  369. $additonalDiskService->create($this->formData);
  370. //Update disk size
  371. $vmModel->disks = $vm->getHardDiskRepostiory()->reset()->additionalSize();
  372. $vmModel->save();
  373. }
  374. }