Przeglądaj źródła

Add VHI provider

Cristian Matiut 11 miesięcy temu
rodzic
commit
681ebaa4cd

+ 18 - 0
config.ts

@@ -75,6 +75,22 @@ const conf: Config = {
       types: ["destination"],
       types: ["destination"],
       requiredFields: ["list_all_destination_networks"],
       requiredFields: ["list_all_destination_networks"],
     },
     },
+    {
+      name: "vhi",
+      types: ["destination"],
+      requiredFields: ["list_all_destination_networks"],
+    },
+    {
+      name: "vhi",
+      types: ["source"],
+      requiredFields: ["replica_export_mechanism"],
+      requiredValues: [
+        {
+          field: "replica_export_mechanism",
+          values: ["swift_backups", "ceph_backups", "coriolis_backups"],
+        },
+      ],
+    },
     {
     {
       name: "aws",
       name: "aws",
       types: ["source", "destination"],
       types: ["source", "destination"],
@@ -126,6 +142,7 @@ const conf: Config = {
   providerSortPriority: {
   providerSortPriority: {
     aws: 1,
     aws: 1,
     openstack: 1,
     openstack: 1,
+    vhi: 2,
     vmware_vsphere: 1,
     vmware_vsphere: 1,
     azure: 2,
     azure: 2,
     "hyper-v": 2,
     "hyper-v": 2,
@@ -146,6 +163,7 @@ const conf: Config = {
   providerNames: {
   providerNames: {
     aws: "AWS",
     aws: "AWS",
     openstack: "OpenStack",
     openstack: "OpenStack",
+    vhi: "VHI",
     vmware_vsphere: "VMware",
     vmware_vsphere: "VMware",
     azure: "Azure",
     azure: "Azure",
     "hyper-v": "Hyper-V",
     "hyper-v": "Hyper-V",

Plik diff jest za duży
+ 4 - 0
server/api/resources/providerLogos/vhi-128-disabled.svg


Plik diff jest za duży
+ 4 - 0
server/api/resources/providerLogos/vhi-128-old.svg


+ 24 - 0
server/api/resources/providerLogos/vhi-128.svg

@@ -0,0 +1,24 @@
+<svg width="192" height="128" viewBox="0 0 192 128" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_348_107" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="92" width="192" height="128">
+<path d="M192 92H0V129.486H192V92Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_348_107)">
+<mask id="mask1_348_107" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="92" width="192" height="128">
+<path d="M192 92H0V129.486H192V92Z" fill="white"/>
+</mask>
+<g mask="url(#mask1_348_107)">
+<path d="M50.0558 107.806H49.8546L49.427 104.213H46.0062V128.439H50.496V113.062C51.5651 110.112 53.7534 108.486 56.8599 108.121V104.138C53.6278 104.024 51.2884 105.663 50.0558 107.806Z" fill="black"/>
+<path d="M0 94.8737L15.1427 128.452H19.3182L34.3855 94.8737H29.2541L17.5323 121.746L17.331 122.377H17.1298L16.9286 121.746L5.11883 94.8737H0Z" fill="black"/>
+<path d="M95.1196 118.57C95.1196 125.1 91.661 129.486 85.0204 129.486C78.3797 129.486 74.8833 125.1 74.9211 118.57V104.138H79.5369V118.117C79.5369 123.222 82.0018 125.213 85.0204 125.213C88.0389 125.213 90.4284 123.222 90.504 118.117V104.138H95.1196V118.57Z" fill="black"/>
+<path d="M38.6623 104.15H33.9711V128.452H38.6623V104.15Z" fill="black"/>
+<path d="M60.0166 119.566C60.0166 125.301 62.7585 128.691 68.481 128.691H69.7513V124.708C65.6511 124.633 64.5444 122.717 64.5444 118.053V108.058H70.4681V104.151H64.5444V94.8737H60.0166V119.566Z" fill="black"/>
+<path d="M128.498 128.44H145.125V124.621H135.177L144.408 104.138H129.102V107.957H137.604L128.498 128.44Z" fill="black"/>
+<path d="M147.666 128.44H164.318V124.621H154.382L163.564 104.138H148.257V107.957H156.772L147.666 128.44Z" fill="black"/>
+<path d="M112.904 103.18C105.509 103.18 99.8993 108.915 99.8993 116.326C99.8993 123.738 105.509 129.473 112.904 129.473C120.299 129.473 125.909 123.738 125.909 116.326C125.909 108.915 120.299 103.18 112.904 103.18ZM112.904 125.212C108.175 125.212 104.716 121.192 104.716 116.326C104.716 111.461 108.175 107.44 112.904 107.44C117.633 107.44 121.092 111.461 121.092 116.326C121.092 121.192 117.633 125.212 112.904 125.212Z" fill="black"/>
+<path d="M178.995 103.18C171.6 103.18 165.991 108.915 165.991 116.326C165.991 123.738 171.6 129.473 178.995 129.473C186.391 129.473 192 123.738 192 116.326C191.987 108.915 186.391 103.18 178.995 103.18ZM178.995 125.212C174.266 125.212 170.808 121.192 170.808 116.326C170.808 111.461 174.266 107.44 178.995 107.44C183.724 107.44 187.183 111.461 187.183 116.326C187.183 121.192 183.724 125.212 178.995 125.212Z" fill="black"/>
+</g>
+</g>
+<path d="M125.761 17.4077C127.539 18.4528 127.538 21.0245 125.76 22.0686L97.5121 38.6487C96.6674 39.1445 95.6205 39.1445 94.7759 38.6487L66.5284 22.0686C64.7496 21.0245 64.7488 18.4528 66.5271 17.4077L94.7744 0.805509C95.6198 0.308646 96.6681 0.308646 97.5135 0.805506L125.761 17.4077Z" fill="#CB333B"/>
+<path d="M100.304 81.7669C100.304 83.8562 102.573 85.1553 104.375 84.0977L132.554 67.5582C133.38 67.0728 133.888 66.1863 133.888 65.2274V32.0706C133.888 29.9814 131.619 28.6822 129.818 29.7397L101.639 46.2793C100.812 46.7647 100.304 47.6512 100.304 48.6101V81.7669Z" fill="#CB333B"/>
+<path d="M91.9895 81.7669C91.9895 83.8562 89.7204 85.1553 87.9187 84.0977L59.74 67.5582C58.9132 67.0728 58.4054 66.1863 58.4054 65.2274V32.0706C58.4054 29.9814 60.6745 28.6822 62.4762 29.7397L90.6548 46.2793C91.4818 46.7647 91.9895 47.6512 91.9895 48.6101V81.7669Z" fill="#CB333B"/>
+</svg>

+ 43 - 0
server/api/resources/providerLogos/vhi-32 copy.svg

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="162px" height="32px" viewBox="0 0 161 32" version="1.1">
+<defs>
+<filter id="alpha" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
+  <feColorMatrix type="matrix" in="SourceGraphic" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
+</filter>
+<image id="image13" width="161" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKEAAAAgCAYAAAB6vRjLAAAABmJLR0QA/wD/AP+gvaeTAAAAe0lEQVR4nO3SuRGAMADAsPD0LMlODEvuwhgukCZw4W3N+xkQ2tY8Vh3Bv+11AJiQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmdY1xvHcG/feT4Bz41fbhwAAAAAElFTkSuQmCC"/>
+<mask id="mask0">
+  <g filter="url(#alpha)">
+<use xlink:href="#image13"/>
+  </g>
+</mask>
+<image id="image8" width="161" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKEAAAAgCAYAAAB6vRjLAAAABmJLR0QA/wD/AP+gvaeTAAAAe0lEQVR4nO3SuRGAMADAsPD0LMlODEvuwhgukCZw4W3N+xkQ2tY8Vh3Bv+11AJiQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmZkJwJyZmQnAnJmZCcCcmdY1xvHcG/feT4Bz41fbhwAAAAAElFTkSuQmCC"/>
+<mask id="mask1">
+  <g filter="url(#alpha)">
+<use xlink:href="#image8"/>
+  </g>
+</mask>
+<clipPath id="clip2">
+  <rect x="0" y="0" width="161" height="32"/>
+</clipPath>
+<g id="surface7" clip-path="url(#clip2)">
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 41.972656 13.71875 L 41.804688 13.71875 L 41.445312 10.695312 L 38.578125 10.695312 L 38.578125 31.09375 L 42.34375 31.09375 L 42.34375 18.144531 C 43.238281 15.660156 45.074219 14.292969 47.679688 13.984375 L 47.679688 10.632812 C 44.96875 10.535156 43.007812 11.914062 41.972656 13.71875 Z M 41.972656 13.71875 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 0 2.828125 L 12.699219 31.105469 L 16.199219 31.105469 L 28.832031 2.828125 L 24.53125 2.828125 L 14.703125 25.457031 L 14.53125 25.992188 L 14.363281 25.992188 L 14.195312 25.457031 L 4.292969 2.828125 Z M 0 2.828125 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 79.761719 22.785156 C 79.761719 28.285156 76.863281 31.976562 71.292969 31.976562 C 65.726562 31.976562 62.792969 28.285156 62.824219 22.785156 L 62.824219 10.632812 L 66.695312 10.632812 L 66.695312 22.402344 C 66.695312 26.703125 68.761719 28.378906 71.292969 28.378906 C 73.824219 28.378906 75.828125 26.703125 75.890625 22.402344 L 75.890625 10.632812 L 79.761719 10.632812 Z M 79.761719 22.785156 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 32.421875 10.640625 L 28.484375 10.640625 L 28.484375 31.105469 L 32.421875 31.105469 Z M 32.421875 10.640625 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 50.328125 23.625 C 50.328125 28.453125 52.625 31.308594 57.425781 31.308594 L 58.488281 31.308594 L 58.488281 27.953125 C 55.050781 27.890625 54.125 26.277344 54.125 22.347656 L 54.125 13.933594 L 59.089844 13.933594 L 59.089844 10.640625 L 54.125 10.640625 L 54.125 2.828125 L 50.328125 2.828125 Z M 50.328125 23.625 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 107.75 31.097656 L 121.695312 31.097656 L 121.695312 27.878906 L 113.351562 27.878906 L 121.09375 10.632812 L 108.257812 10.632812 L 108.257812 13.847656 L 115.386719 13.847656 Z M 107.75 31.097656 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 123.824219 31.097656 L 137.789062 31.097656 L 137.789062 27.878906 L 129.457031 27.878906 L 137.15625 10.632812 L 124.320312 10.632812 L 124.320312 13.847656 L 131.460938 13.847656 Z M 123.824219 31.097656 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 94.675781 9.824219 C 88.472656 9.824219 83.769531 14.652344 83.769531 20.894531 C 83.769531 27.136719 88.472656 31.964844 94.675781 31.964844 C 100.875 31.964844 105.578125 27.136719 105.578125 20.894531 C 105.578125 14.652344 100.875 9.824219 94.675781 9.824219 Z M 94.675781 28.378906 C 90.710938 28.378906 87.808594 24.992188 87.808594 20.894531 C 87.808594 16.796875 90.710938 13.410156 94.675781 13.410156 C 98.640625 13.410156 101.539062 16.796875 101.539062 20.894531 C 101.539062 24.992188 98.640625 28.378906 94.675781 28.378906 Z M 94.675781 28.378906 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 150.09375 9.824219 C 143.894531 9.824219 139.191406 14.652344 139.191406 20.894531 C 139.191406 27.136719 143.894531 31.964844 150.09375 31.964844 C 156.296875 31.964844 161 27.136719 161 20.894531 C 160.988281 14.652344 156.296875 9.824219 150.09375 9.824219 Z M 150.09375 28.378906 C 146.128906 28.378906 143.230469 24.992188 143.230469 20.894531 C 143.230469 16.796875 146.128906 13.410156 150.09375 13.410156 C 154.058594 13.410156 156.960938 16.796875 156.960938 20.894531 C 156.960938 24.992188 154.058594 28.378906 150.09375 28.378906 Z M 150.09375 28.378906 "/>
+</g>
+<clipPath id="clip1">
+  <rect x="0" y="0" width="161" height="32"/>
+</clipPath>
+<g id="surface12" clip-path="url(#clip1)">
+<use xlink:href="#surface7" mask="url(#mask1)"/>
+</g>
+</defs>
+<g id="surface1">
+<use xlink:href="#surface12" mask="url(#mask0)"/>
+</g>
+</svg>

Plik diff jest za duży
+ 4 - 0
server/api/resources/providerLogos/vhi-32-white.svg


+ 21 - 0
server/api/resources/providerLogos/vhi-32.svg

@@ -0,0 +1,21 @@
+<svg width="162" height="32" viewBox="0 0 162 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="162" height="32">
+<path d="M192 0.48645H0V37.9721H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_94_77)">
+<mask id="mask1_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="162" height="32">
+<path d="M192 0.48645H0V37.9725H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask1_94_77)">
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 41.972656 13.71875 L 41.804688 13.71875 L 41.445312 10.695312 L 38.578125 10.695312 L 38.578125 31.09375 L 42.34375 31.09375 L 42.34375 18.144531 C 43.238281 15.660156 45.074219 14.292969 47.679688 13.984375 L 47.679688 10.632812 C 44.96875 10.535156 43.007812 11.914062 41.972656 13.71875 Z M 41.972656 13.71875 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 0 2.828125 L 12.699219 31.105469 L 16.199219 31.105469 L 28.832031 2.828125 L 24.53125 2.828125 L 14.703125 25.457031 L 14.53125 25.992188 L 14.363281 25.992188 L 14.195312 25.457031 L 4.292969 2.828125 Z M 0 2.828125 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 79.761719 22.785156 C 79.761719 28.285156 76.863281 31.976562 71.292969 31.976562 C 65.726562 31.976562 62.792969 28.285156 62.824219 22.785156 L 62.824219 10.632812 L 66.695312 10.632812 L 66.695312 22.402344 C 66.695312 26.703125 68.761719 28.378906 71.292969 28.378906 C 73.824219 28.378906 75.828125 26.703125 75.890625 22.402344 L 75.890625 10.632812 L 79.761719 10.632812 Z M 79.761719 22.785156 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 32.421875 10.640625 L 28.484375 10.640625 L 28.484375 31.105469 L 32.421875 31.105469 Z M 32.421875 10.640625 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 50.328125 23.625 C 50.328125 28.453125 52.625 31.308594 57.425781 31.308594 L 58.488281 31.308594 L 58.488281 27.953125 C 55.050781 27.890625 54.125 26.277344 54.125 22.347656 L 54.125 13.933594 L 59.089844 13.933594 L 59.089844 10.640625 L 54.125 10.640625 L 54.125 2.828125 L 50.328125 2.828125 Z M 50.328125 23.625 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 107.75 31.097656 L 121.695312 31.097656 L 121.695312 27.878906 L 113.351562 27.878906 L 121.09375 10.632812 L 108.257812 10.632812 L 108.257812 13.847656 L 115.386719 13.847656 Z M 107.75 31.097656 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 123.824219 31.097656 L 137.789062 31.097656 L 137.789062 27.878906 L 129.457031 27.878906 L 137.15625 10.632812 L 124.320312 10.632812 L 124.320312 13.847656 L 131.460938 13.847656 Z M 123.824219 31.097656 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 94.675781 9.824219 C 88.472656 9.824219 83.769531 14.652344 83.769531 20.894531 C 83.769531 27.136719 88.472656 31.964844 94.675781 31.964844 C 100.875 31.964844 105.578125 27.136719 105.578125 20.894531 C 105.578125 14.652344 100.875 9.824219 94.675781 9.824219 Z M 94.675781 28.378906 C 90.710938 28.378906 87.808594 24.992188 87.808594 20.894531 C 87.808594 16.796875 90.710938 13.410156 94.675781 13.410156 C 98.640625 13.410156 101.539062 16.796875 101.539062 20.894531 C 101.539062 24.992188 98.640625 28.378906 94.675781 28.378906 Z M 94.675781 28.378906 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 150.09375 9.824219 C 143.894531 9.824219 139.191406 14.652344 139.191406 20.894531 C 139.191406 27.136719 143.894531 31.964844 150.09375 31.964844 C 156.296875 31.964844 161 27.136719 161 20.894531 C 160.988281 14.652344 156.296875 9.824219 150.09375 9.824219 Z M 150.09375 28.378906 C 146.128906 28.378906 143.230469 24.992188 143.230469 20.894531 C 143.230469 16.796875 146.128906 13.410156 150.09375 13.410156 C 154.058594 13.410156 156.960938 16.796875 156.960938 20.894531 C 156.960938 24.992188 154.058594 28.378906 150.09375 28.378906 Z M 150.09375 28.378906 "/>
+</g>
+</g>
+</svg>

+ 21 - 0
server/api/resources/providerLogos/vhi-42.svg

@@ -0,0 +1,21 @@
+<svg width="212" height="42" viewBox="0 0 212 42" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="212" height="42">
+<path d="M192 0.48645H0V37.9721H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_94_77)">
+<mask id="mask1_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="212" height="42">
+<path d="M192 0.48645H0V37.9725H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask1_94_77)">
+<path d="M50.0558 16.2925H49.8546L49.427 12.6995H46.0062V36.9255H50.496V21.5485C51.5651 18.5985 53.7534 16.9725 56.8599 16.6075V12.6245C53.6278 12.5105 51.2884 14.1495 50.0558 16.2925Z" fill="black"/>
+<path d="M0 3.36011L15.1427 36.9385H19.3182L34.3855 3.36011H29.2541L17.5323 30.2325L17.331 30.8635H17.1298L16.9286 30.2325L5.11883 3.36011H0Z" fill="black"/>
+<path d="M95.1196 27.0565C95.1196 33.5865 91.661 37.9725 85.0204 37.9725C78.3797 37.9725 74.8833 33.5865 74.9211 27.0565V12.6245H79.5369V26.6035C79.5369 31.7085 82.0018 33.6995 85.0204 33.6995C88.0389 33.6995 90.4284 31.7085 90.504 26.6035V12.6245H95.1196V27.0565Z" fill="black"/>
+<path d="M38.6623 12.6365H33.9711V36.9385H38.6623V12.6365Z" fill="black"/>
+<path d="M60.0166 28.0525C60.0166 33.7875 62.7585 37.1775 68.481 37.1775H69.7513V33.1945C65.6511 33.1195 64.5444 31.2035 64.5444 26.5395V16.5445H70.4681V12.6375H64.5444V3.36011H60.0166V28.0525Z" fill="black"/>
+<path d="M128.498 36.9265H145.125V33.1075H135.177L144.408 12.6245H129.102V16.4435H137.604L128.498 36.9265Z" fill="black"/>
+<path d="M147.666 36.9265H164.318V33.1075H154.382L163.564 12.6245H148.257V16.4435H156.772L147.666 36.9265Z" fill="black"/>
+<path d="M112.904 11.6665C105.509 11.6665 99.8993 17.4015 99.8993 24.8125C99.8993 32.2245 105.509 37.9595 112.904 37.9595C120.299 37.9595 125.909 32.2245 125.909 24.8125C125.909 17.4015 120.299 11.6665 112.904 11.6665ZM112.904 33.6985C108.175 33.6985 104.716 29.6785 104.716 24.8125C104.716 19.9475 108.175 15.9265 112.904 15.9265C117.633 15.9265 121.092 19.9475 121.092 24.8125C121.092 29.6785 117.633 33.6985 112.904 33.6985Z" fill="black"/>
+<path d="M178.995 11.6665C171.6 11.6665 165.991 17.4015 165.991 24.8125C165.991 32.2245 171.6 37.9595 178.995 37.9595C186.391 37.9595 192 32.2245 192 24.8125C191.987 17.4015 186.391 11.6665 178.995 11.6665ZM178.995 33.6985C174.266 33.6985 170.808 29.6785 170.808 24.8125C170.808 19.9475 174.266 15.9265 178.995 15.9265C183.724 15.9265 187.183 19.9475 187.183 24.8125C187.183 29.6785 183.724 33.6985 178.995 33.6985Z" fill="black"/>
+</g>
+</g>
+</svg>

+ 21 - 0
server/api/resources/providerLogos/vhi-64.svg

@@ -0,0 +1,21 @@
+<svg width="323" height="64" viewBox="0 0 192 38" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="323" height="64">
+<path d="M192 0.48645H0V37.9721H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_94_77)">
+<mask id="mask1_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="323" height="64">
+<path d="M192 0.48645H0V37.9725H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask1_94_77)">
+<path d="M50.0558 16.2925H49.8546L49.427 12.6995H46.0062V36.9255H50.496V21.5485C51.5651 18.5985 53.7534 16.9725 56.8599 16.6075V12.6245C53.6278 12.5105 51.2884 14.1495 50.0558 16.2925Z" fill="black"/>
+<path d="M0 3.36011L15.1427 36.9385H19.3182L34.3855 3.36011H29.2541L17.5323 30.2325L17.331 30.8635H17.1298L16.9286 30.2325L5.11883 3.36011H0Z" fill="black"/>
+<path d="M95.1196 27.0565C95.1196 33.5865 91.661 37.9725 85.0204 37.9725C78.3797 37.9725 74.8833 33.5865 74.9211 27.0565V12.6245H79.5369V26.6035C79.5369 31.7085 82.0018 33.6995 85.0204 33.6995C88.0389 33.6995 90.4284 31.7085 90.504 26.6035V12.6245H95.1196V27.0565Z" fill="black"/>
+<path d="M38.6623 12.6365H33.9711V36.9385H38.6623V12.6365Z" fill="black"/>
+<path d="M60.0166 28.0525C60.0166 33.7875 62.7585 37.1775 68.481 37.1775H69.7513V33.1945C65.6511 33.1195 64.5444 31.2035 64.5444 26.5395V16.5445H70.4681V12.6375H64.5444V3.36011H60.0166V28.0525Z" fill="black"/>
+<path d="M128.498 36.9265H145.125V33.1075H135.177L144.408 12.6245H129.102V16.4435H137.604L128.498 36.9265Z" fill="black"/>
+<path d="M147.666 36.9265H164.318V33.1075H154.382L163.564 12.6245H148.257V16.4435H156.772L147.666 36.9265Z" fill="black"/>
+<path d="M112.904 11.6665C105.509 11.6665 99.8993 17.4015 99.8993 24.8125C99.8993 32.2245 105.509 37.9595 112.904 37.9595C120.299 37.9595 125.909 32.2245 125.909 24.8125C125.909 17.4015 120.299 11.6665 112.904 11.6665ZM112.904 33.6985C108.175 33.6985 104.716 29.6785 104.716 24.8125C104.716 19.9475 108.175 15.9265 112.904 15.9265C117.633 15.9265 121.092 19.9475 121.092 24.8125C121.092 29.6785 117.633 33.6985 112.904 33.6985Z" fill="black"/>
+<path d="M178.995 11.6665C171.6 11.6665 165.991 17.4015 165.991 24.8125C165.991 32.2245 171.6 37.9595 178.995 37.9595C186.391 37.9595 192 32.2245 192 24.8125C191.987 17.4015 186.391 11.6665 178.995 11.6665ZM178.995 33.6985C174.266 33.6985 170.808 29.6785 170.808 24.8125C170.808 19.9475 174.266 15.9265 178.995 15.9265C183.724 15.9265 187.183 19.9475 187.183 24.8125C187.183 29.6785 183.724 33.6985 178.995 33.6985Z" fill="black"/>
+</g>
+</g>
+</svg>

+ 21 - 0
server/api/resources/providerLogos/virtuozzo-word.svg

@@ -0,0 +1,21 @@
+<svg width="192" height="38" viewBox="0 0 192 38" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="192" height="38">
+<path d="M192 0.48645H0V37.9721H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_94_77)">
+<mask id="mask1_94_77" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="192" height="38">
+<path d="M192 0.48645H0V37.9725H192V0.48645Z" fill="white"/>
+</mask>
+<g mask="url(#mask1_94_77)">
+<path d="M50.0558 16.2925H49.8546L49.427 12.6995H46.0062V36.9255H50.496V21.5485C51.5651 18.5985 53.7534 16.9725 56.8599 16.6075V12.6245C53.6278 12.5105 51.2884 14.1495 50.0558 16.2925Z" fill="black"/>
+<path d="M0 3.36011L15.1427 36.9385H19.3182L34.3855 3.36011H29.2541L17.5323 30.2325L17.331 30.8635H17.1298L16.9286 30.2325L5.11883 3.36011H0Z" fill="black"/>
+<path d="M95.1196 27.0565C95.1196 33.5865 91.661 37.9725 85.0204 37.9725C78.3797 37.9725 74.8833 33.5865 74.9211 27.0565V12.6245H79.5369V26.6035C79.5369 31.7085 82.0018 33.6995 85.0204 33.6995C88.0389 33.6995 90.4284 31.7085 90.504 26.6035V12.6245H95.1196V27.0565Z" fill="black"/>
+<path d="M38.6623 12.6365H33.9711V36.9385H38.6623V12.6365Z" fill="black"/>
+<path d="M60.0166 28.0525C60.0166 33.7875 62.7585 37.1775 68.481 37.1775H69.7513V33.1945C65.6511 33.1195 64.5444 31.2035 64.5444 26.5395V16.5445H70.4681V12.6375H64.5444V3.36011H60.0166V28.0525Z" fill="black"/>
+<path d="M128.498 36.9265H145.125V33.1075H135.177L144.408 12.6245H129.102V16.4435H137.604L128.498 36.9265Z" fill="black"/>
+<path d="M147.666 36.9265H164.318V33.1075H154.382L163.564 12.6245H148.257V16.4435H156.772L147.666 36.9265Z" fill="black"/>
+<path d="M112.904 11.6665C105.509 11.6665 99.8993 17.4015 99.8993 24.8125C99.8993 32.2245 105.509 37.9595 112.904 37.9595C120.299 37.9595 125.909 32.2245 125.909 24.8125C125.909 17.4015 120.299 11.6665 112.904 11.6665ZM112.904 33.6985C108.175 33.6985 104.716 29.6785 104.716 24.8125C104.716 19.9475 108.175 15.9265 112.904 15.9265C117.633 15.9265 121.092 19.9475 121.092 24.8125C121.092 29.6785 117.633 33.6985 112.904 33.6985Z" fill="black"/>
+<path d="M178.995 11.6665C171.6 11.6665 165.991 17.4015 165.991 24.8125C165.991 32.2245 171.6 37.9595 178.995 37.9595C186.391 37.9595 192 32.2245 192 24.8125C191.987 17.4015 186.391 11.6665 178.995 11.6665ZM178.995 33.6985C174.266 33.6985 170.808 29.6785 170.808 24.8125C170.808 19.9475 174.266 15.9265 178.995 15.9265C183.724 15.9265 187.183 19.9475 187.183 24.8125C187.183 29.6785 183.724 33.6985 178.995 33.6985Z" fill="black"/>
+</g>
+</g>
+</svg>

+ 5 - 0
server/api/resources/providerLogos/vz-logo.svg

@@ -0,0 +1,5 @@
+<svg width="87" height="87" viewBox="0 0 87 87" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M72.7611 17.4077C74.5395 18.4528 74.5384 21.0245 72.7596 22.0686L44.5121 38.6487C43.6674 39.1445 42.6205 39.1445 41.7759 38.6487L13.5284 22.0686C11.7496 21.0245 11.7488 18.4528 13.5271 17.4077L41.7744 0.805509C42.6198 0.308646 43.6681 0.308646 44.5135 0.805506L72.7611 17.4077Z" fill="#CB333B"/>
+<path d="M47.3044 81.7669C47.3044 83.8562 49.5733 85.1553 51.3751 84.0977L79.5538 67.5582C80.3805 67.0728 80.8882 66.1863 80.8882 65.2274V32.0706C80.8882 29.9814 78.619 28.6822 76.8176 29.7397L48.6389 46.2793C47.8122 46.7647 47.3044 47.6512 47.3044 48.6101V81.7669Z" fill="#CB333B"/>
+<path d="M38.9895 81.7669C38.9895 83.8562 36.7204 85.1553 34.9187 84.0977L6.73999 67.5582C5.91322 67.0728 5.4054 66.1863 5.4054 65.2274V32.0706C5.4054 29.9814 7.67448 28.6822 9.47621 29.7397L37.6548 46.2793C38.4818 46.7647 38.9895 47.6512 38.9895 48.6101V81.7669Z" fill="#CB333B"/>
+</svg>

+ 1 - 0
src/@types/Providers.ts

@@ -15,6 +15,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 export type ProviderTypes =
 export type ProviderTypes =
   | "azure"
   | "azure"
   | "openstack"
   | "openstack"
+  | "vhi"
   | "opc"
   | "opc"
   | "opca"
   | "opca"
   | "o3c"
   | "o3c"

+ 8 - 0
src/plugins/index.ts

@@ -16,6 +16,7 @@ import { ProviderTypes } from "@src/@types/Providers";
 import DefaultConnectionSchemaPlugin from "./default/ConnectionSchemaPlugin";
 import DefaultConnectionSchemaPlugin from "./default/ConnectionSchemaPlugin";
 import AzureConnectionSchemaPlugin from "./azure/ConnectionSchemaPlugin";
 import AzureConnectionSchemaPlugin from "./azure/ConnectionSchemaPlugin";
 import OpenstackConnectionSchemaPlugin from "./openstack/ConnectionSchemaPlugin";
 import OpenstackConnectionSchemaPlugin from "./openstack/ConnectionSchemaPlugin";
+import VHIConnectionSchemaPlugin from "./vhi/ConnectionSchemaPlugin";
 import OciConnectionSchemaPlugin from "./oci/ConnectionSchemaPlugin";
 import OciConnectionSchemaPlugin from "./oci/ConnectionSchemaPlugin";
 import OpcaConnectionSchemaPlugin from "./opca/ConnectionSchemaPlugin";
 import OpcaConnectionSchemaPlugin from "./opca/ConnectionSchemaPlugin";
 import O3cConnectionSchemaPlugin from "./o3c/ConnectionSchemaPlugin";
 import O3cConnectionSchemaPlugin from "./o3c/ConnectionSchemaPlugin";
@@ -24,6 +25,7 @@ import KubevirtConnectionSchemaPlugin from "./kubevirt/ConnectionSchemaPlugin";
 import DefaultContentPlugin from "./default/ContentPlugin";
 import DefaultContentPlugin from "./default/ContentPlugin";
 import AzureContentPlugin from "./azure/ContentPlugin";
 import AzureContentPlugin from "./azure/ContentPlugin";
 import OpenstackContentPlugin from "./openstack/ContentPlugin";
 import OpenstackContentPlugin from "./openstack/ContentPlugin";
+import VHIContentPlugin from "./vhi/ContentPlugin";
 import MetalContentPlugin from "./metal/ContentPlugin";
 import MetalContentPlugin from "./metal/ContentPlugin";
 
 
 import DefaultOptionsSchemaPlugin from "./default/OptionsSchemaPlugin";
 import DefaultOptionsSchemaPlugin from "./default/OptionsSchemaPlugin";
@@ -31,6 +33,7 @@ import AwsOptionsSchemaPlugin from "./aws/OptionsSchemaPlugin";
 import OvmOptionsSchemaPlugin from "./ovm/OptionsSchemaPlugin";
 import OvmOptionsSchemaPlugin from "./ovm/OptionsSchemaPlugin";
 import VmwareOptionsSchemaPlugin from "./vmware_vsphere/OptionsSchemaPlugin";
 import VmwareOptionsSchemaPlugin from "./vmware_vsphere/OptionsSchemaPlugin";
 import OpenstackOptionsSchemaPlugin from "./openstack/OptionsSchemaPlugin";
 import OpenstackOptionsSchemaPlugin from "./openstack/OptionsSchemaPlugin";
+import VHIOptionsSchemaPlugin from "./vhi/OptionsSchemaPlugin";
 import OlvmOptionsSchemaPlugin from "./olvm/OptionsSchemaPlugin";
 import OlvmOptionsSchemaPlugin from "./olvm/OptionsSchemaPlugin";
 import RhevOptionsSchemaPlugin from "./rhev/OptionsSchemaPlugin";
 import RhevOptionsSchemaPlugin from "./rhev/OptionsSchemaPlugin";
 import AzureOptionsSchemaPlugin from "./azure/OptionsSchemaPlugin";
 import AzureOptionsSchemaPlugin from "./azure/OptionsSchemaPlugin";
@@ -40,6 +43,7 @@ import OciInstanceInfoPlugin from "./oci/InstanceInfoPlugin";
 
 
 import DefaultMinionPoolSchemaPlugin from "./default/MinionPoolSchemaPlugin";
 import DefaultMinionPoolSchemaPlugin from "./default/MinionPoolSchemaPlugin";
 import OpenstackMinionPoolSchemaPlugin from "./openstack/MinionPoolSchemaPlugin";
 import OpenstackMinionPoolSchemaPlugin from "./openstack/MinionPoolSchemaPlugin";
+import VHIMinionPoolSchemaPlugin from "./vhi/MinionPoolSchemaPlugin";
 
 
 const hasKey = <O extends object>(obj: O, key: keyof any): key is keyof O =>
 const hasKey = <O extends object>(obj: O, key: keyof any): key is keyof O =>
   key in obj;
   key in obj;
@@ -50,6 +54,7 @@ export const ConnectionSchemaPlugin = {
       default: new DefaultConnectionSchemaPlugin(),
       default: new DefaultConnectionSchemaPlugin(),
       azure: new AzureConnectionSchemaPlugin(),
       azure: new AzureConnectionSchemaPlugin(),
       openstack: new OpenstackConnectionSchemaPlugin(),
       openstack: new OpenstackConnectionSchemaPlugin(),
+      vhi: new VHIConnectionSchemaPlugin(),
       oci: new OciConnectionSchemaPlugin(),
       oci: new OciConnectionSchemaPlugin(),
       opca: new OpcaConnectionSchemaPlugin(),
       opca: new OpcaConnectionSchemaPlugin(),
       o3c: new O3cConnectionSchemaPlugin(),
       o3c: new O3cConnectionSchemaPlugin(),
@@ -69,6 +74,7 @@ export const OptionsSchemaPlugin = {
       aws: new AwsOptionsSchemaPlugin(),
       aws: new AwsOptionsSchemaPlugin(),
       oracle_vm: new OvmOptionsSchemaPlugin(),
       oracle_vm: new OvmOptionsSchemaPlugin(),
       openstack: new OpenstackOptionsSchemaPlugin(),
       openstack: new OpenstackOptionsSchemaPlugin(),
+      vhi: new VHIOptionsSchemaPlugin(),
       vmware_vsphere: new VmwareOptionsSchemaPlugin(),
       vmware_vsphere: new VmwareOptionsSchemaPlugin(),
       olvm: new OlvmOptionsSchemaPlugin(),
       olvm: new OlvmOptionsSchemaPlugin(),
       rhev: new RhevOptionsSchemaPlugin(),
       rhev: new RhevOptionsSchemaPlugin(),
@@ -87,6 +93,7 @@ export const ContentPlugin = {
       default: DefaultContentPlugin,
       default: DefaultContentPlugin,
       azure: AzureContentPlugin,
       azure: AzureContentPlugin,
       openstack: OpenstackContentPlugin,
       openstack: OpenstackContentPlugin,
+      vhi: VHIContentPlugin,
       metal: MetalContentPlugin,
       metal: MetalContentPlugin,
     };
     };
     if (hasKey(map, provider)) {
     if (hasKey(map, provider)) {
@@ -114,6 +121,7 @@ export const MinionPoolSchemaPlugin = {
     const map = {
     const map = {
       default: new DefaultMinionPoolSchemaPlugin(),
       default: new DefaultMinionPoolSchemaPlugin(),
       openstack: new OpenstackMinionPoolSchemaPlugin(),
       openstack: new OpenstackMinionPoolSchemaPlugin(),
+      vhi: new VHIMinionPoolSchemaPlugin(),
     };
     };
     if (hasKey(map, provider)) {
     if (hasKey(map, provider)) {
       return map[provider];
       return map[provider];

+ 125 - 0
src/plugins/vhi/ConnectionSchemaPlugin.ts

@@ -0,0 +1,125 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import type { Schema } from "@src/@types/Schema";
+import type { Field } from "@src/@types/Field";
+import type { Endpoint } from "@src/@types/Endpoint";
+import ConnectionSchemaParserBase from "@src/plugins/default/ConnectionSchemaPlugin";
+
+const customSort = (fields: Field[]) => {
+  const sortPriority: any = {
+    name: 1,
+    mapped_regions: 1.5,
+    description: 2,
+    username: 3,
+    password: 4,
+    auth_url: 5,
+    project_name: 6,
+    glance_api_version: 7,
+    identity_api_version: 8,
+    project_domain: 9,
+    user_domain: 10,
+  };
+  fields.sort((a, b) => {
+    if (sortPriority[a.name] && sortPriority[b.name]) {
+      return sortPriority[a.name] - sortPriority[b.name];
+    }
+    if (sortPriority[a.name]) {
+      return -1;
+    }
+    if (sortPriority[b.name]) {
+      return 1;
+    }
+    return a.name.localeCompare(b.name);
+  });
+
+  return fields;
+};
+
+export default class ConnectionSchemaParser extends ConnectionSchemaParserBase {
+  override parseSchemaToFields(schema: Schema): Field[] {
+    const fields = super.parseSchemaToFields(schema);
+    const identityField = fields.find(f => f.name === "identity_api_version");
+    if (identityField && !identityField.default) {
+      identityField.default = identityField.minimum;
+    }
+
+    fields
+      .find(f => f.name === "ceph_options")
+      ?.properties?.forEach(f => {
+        f.name = `ceph_options/${f.name}`;
+      });
+
+    const createInputChoice = (
+      name: string,
+      field1Name: string,
+      field2Name: string
+    ) => {
+      const field1 = fields.find(f => f.name === field1Name);
+      const field2 = fields.find(f => f.name === field2Name);
+      if (field1 && field2) {
+        field1.label = "Name";
+        field2.label = "ID";
+        const field: Field = {
+          name,
+          type: "input-choice",
+          items: [field1, field2],
+        };
+        fields.push(field);
+      }
+    };
+    createInputChoice(
+      "project_domain",
+      "project_domain_name",
+      "project_domain_id"
+    );
+    createInputChoice("user_domain", "user_domain_name", "user_domain_id");
+
+    customSort(fields);
+    fields.push({
+      name: "openstack_use_current_user",
+      type: "boolean",
+    });
+    return fields;
+  }
+
+  override parseConnectionInfoToPayload(
+    data: { [prop: string]: any },
+    schema: Schema
+  ) {
+    if (data.openstack_use_current_user) {
+      return {};
+    }
+    // eslint-disable-next-line no-param-reassign
+    delete data.project_domain;
+    // eslint-disable-next-line no-param-reassign
+    delete data.user_domain;
+    const payload = super.parseConnectionInfoToPayload(data, schema);
+    return payload;
+  }
+
+  override parseConnectionResponse(endpoint: Endpoint) {
+    if (
+      !endpoint.connection_info ||
+      Object.keys(endpoint.connection_info).length === 0
+    ) {
+      return {
+        openstack_use_current_user: true,
+        ...endpoint,
+      };
+    }
+
+    return endpoint;
+  }
+}

+ 465 - 0
src/plugins/vhi/ContentPlugin.tsx

@@ -0,0 +1,465 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import * as React from "react";
+import styled from "styled-components";
+
+import configLoader from "@src/utils/Config";
+import LabelDictionary from "@src/utils/LabelDictionary";
+
+import ToggleButtonBar from "@src/components/ui/ToggleButtonBar";
+import type { Field } from "@src/@types/Field";
+import { Wrapper, FieldStyled, Row } from "@src/plugins/default/ContentPlugin";
+
+import { Validation, Endpoint } from "@src/@types/Endpoint";
+import { ThemePalette, ThemeProps } from "@src/components/Theme";
+
+const ToggleButtonBarStyled = styled(ToggleButtonBar)`
+  margin-top: 16px;
+`;
+const Fields = styled.div<any>`
+  margin-top: 32px;
+  padding: 0 32px;
+  display: flex;
+  flex-direction: column;
+  overflow: auto;
+`;
+const Group = styled.div<any>`
+  display: flex;
+  flex-direction: column;
+  flex-shrink: 0;
+`;
+const GroupName = styled.div<any>`
+  display: flex;
+  align-items: center;
+  margin: 32px 0 24px 0;
+`;
+const GroupNameText = styled.div<any>`
+  margin: 0 32px;
+  font-size: 16px;
+`;
+const GroupNameBar = styled.div<any>`
+  flex-grow: 1;
+  background: ${ThemePalette.grayscale[3]};
+  height: 1px;
+`;
+const GroupFields = styled.div<any>`
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+`;
+
+type Props = {
+  connectionInfoSchema: Field[];
+  validation: Validation | null;
+  invalidFields: string[];
+  getFieldValue: (field?: Field | null) => any;
+  handleFieldChange: (field: Field | null | undefined, value: any) => void;
+  handleFieldsChange: (items: { field: Field; value: any }[]) => void;
+  disabled: boolean;
+  cancelButtonText: string;
+  validating: boolean;
+  onRef: (contentPlugin: any) => void;
+  onResizeUpdate: (scrollOfset: number) => void;
+  scrollableRef: (ref: HTMLElement) => void;
+  originalConnectionInfo: Endpoint["connection_info"];
+  highlightRequired: () => void;
+  handleValidateClick: () => void;
+  handleCancelClick: () => void;
+};
+type State = {
+  useAdvancedOptions: boolean;
+  showCephOptions: boolean;
+};
+class ContentPlugin extends React.Component<Props, State> {
+  // This is a temporary hack, should be always true for all plugins,
+  // but momentaraly causes issues in Azure plugins
+  // Fix Azure plugin and remove this line
+  static REQUIRES_PARENT_OBJECT_PATH = true;
+
+  state = {
+    useAdvancedOptions: false,
+    showCephOptions: false,
+  };
+
+  previouslySelectedChoices: string[] = [];
+
+  componentDidMount() {
+    this.props.onRef(this);
+  }
+
+  componentDidUpdate(_: Props, prevState: State) {
+    if (prevState.useAdvancedOptions !== this.state.useAdvancedOptions) {
+      this.props.onResizeUpdate(0);
+    }
+  }
+
+  componentWillUnmount() {
+    this.props.onRef(undefined);
+  }
+
+  get useCurrentUser(): boolean {
+    return Boolean(
+      this.getFieldValue(
+        this.props.connectionInfoSchema.find(
+          n => n.name === "openstack_use_current_user"
+        )
+      )
+    );
+  }
+
+  get hasCephOptionsSet(): boolean {
+    const cephOptionsField = this.props.connectionInfoSchema.find(
+      n => n.name === "ceph_options"
+    );
+    if (!cephOptionsField || !cephOptionsField.properties) {
+      return false;
+    }
+    const hasValues = cephOptionsField.properties.filter(f =>
+      this.getFieldValue(f)
+    );
+    return hasValues.length > 0;
+  }
+
+  getApiVersion(): number {
+    return this.props.getFieldValue(
+      this.props.connectionInfoSchema.find(
+        n => n.name === "identity_api_version"
+      )
+    );
+  }
+
+  getFieldValue(field?: Field | null) {
+    const fieldValue = this.props.getFieldValue(field);
+    if (fieldValue) {
+      return fieldValue;
+    }
+
+    const getInputChoiceValue = (fieldBaseName: string): string => {
+      const id = this.props.getFieldValue(
+        this.props.connectionInfoSchema.find(
+          n => n.name === `${fieldBaseName}_id`
+        )
+      );
+      const previouslySelected = this.previouslySelectedChoices.find(
+        f => f === `${fieldBaseName}_id`
+      );
+      if (id || previouslySelected) {
+        if (!previouslySelected)
+          this.previouslySelectedChoices.push(`${fieldBaseName}_id`);
+        return `${fieldBaseName}_id`;
+      }
+      return `${fieldBaseName}_name`;
+    };
+    if (field && field.name === "user_domain") {
+      return getInputChoiceValue("user_domain");
+    }
+    if (field && field.name === "project_domain") {
+      return getInputChoiceValue("project_domain");
+    }
+
+    return fieldValue;
+  }
+
+  // eslint-disable-next-line react/no-unused-class-component-methods
+  findInvalidFields = () => {
+    const inputChoices = ["user_domain", "project_domain"];
+
+    let invalidFields = this.props.connectionInfoSchema
+      .filter(field => {
+        if (this.isFieldRequired(field)) {
+          const value = this.getFieldValue(field);
+          return !value || value.length === 0;
+        }
+        const inputChoice = inputChoices.find(c => c === field.name);
+        if (inputChoice && this.getApiVersion() > 2) {
+          const selectionValue = this.getFieldValue(
+            this.props.connectionInfoSchema.find(f => f.name === inputChoice)
+          );
+          const itemValue = this.getFieldValue(
+            this.props.connectionInfoSchema.find(f => f.name === selectionValue)
+          );
+          return !itemValue;
+        }
+
+        return false;
+      })
+      .map(f => f.name);
+
+    const cephOptions = this.props.connectionInfoSchema.find(
+      f => f.name === "ceph_options"
+    );
+    const cephOptionsProperties = cephOptions && cephOptions.properties;
+    if (
+      cephOptionsProperties &&
+      (this.state.showCephOptions || this.hasCephOptionsSet)
+    ) {
+      invalidFields = invalidFields.concat(
+        cephOptionsProperties
+          .filter(f => f.required && !this.getFieldValue(f))
+          .map(f => f.name)
+      );
+    }
+    return invalidFields;
+  };
+
+  handleAdvancedOptionsToggle(useAdvancedOptions: boolean) {
+    this.setState({ useAdvancedOptions });
+  }
+
+  handleShowCepthOptionsChange(value: boolean) {
+    const cephOptions = this.props.connectionInfoSchema.find(
+      f => f.name === "ceph_options"
+    );
+    if (!cephOptions || !cephOptions.properties) {
+      return;
+    }
+    const resetFields = cephOptions.properties.map(field => ({
+      field,
+      value: null,
+    }));
+
+    this.props.handleFieldsChange(resetFields);
+
+    this.setState({ showCephOptions: value });
+  }
+
+  filterSimpleAdvanced(): Field[] {
+    let extraAdvancedFields = [
+      "description",
+      "glance_api_version",
+      "identity_api_version",
+      "openstack_use_current_user",
+    ];
+    if (this.getApiVersion() > 2) {
+      extraAdvancedFields = extraAdvancedFields.concat([
+        "user_domain",
+        "project_domain",
+      ]);
+    }
+    const ignoreFields = [
+      "user_domain_id",
+      "project_domain_id",
+      "user_domain_name",
+      "project_domain_name",
+    ];
+    if (!configLoader.config.showOpenstackCurrentUserSwitch) {
+      ignoreFields.push("openstack_use_current_user");
+    }
+
+    return this.props.connectionInfoSchema
+      .filter(f => !ignoreFields.find(i => i === f.name))
+      .filter(field => {
+        if (field.name === "ceph_options") {
+          return (
+            this.state.useAdvancedOptions &&
+            (this.state.showCephOptions || this.hasCephOptionsSet)
+          );
+        }
+
+        if (this.state.useAdvancedOptions) {
+          return true;
+        }
+        return (
+          field.required ||
+          extraAdvancedFields.find(fieldName => field.name === fieldName)
+        );
+      });
+  }
+
+  isFieldRequired(field: Field) {
+    return this.useCurrentUser ? field.name === "name" : field.required;
+  }
+
+  renderSimpleAdvancedToggle() {
+    return (
+      <ToggleButtonBarStyled
+        items={[
+          { label: "Simple", value: "simple" },
+          { label: "Advanced", value: "advanced" },
+        ]}
+        selectedValue={this.state.useAdvancedOptions ? "advanced" : "simple"}
+        onChange={item => {
+          this.handleAdvancedOptionsToggle(item.value === "advanced");
+        }}
+      />
+    );
+  }
+
+  renderFields() {
+    const rows: JSX.Element[] = [];
+    const fields = this.filterSimpleAdvanced();
+    if (this.state.useAdvancedOptions) {
+      const showCepthOptionsField = {
+        name: "show_ceph_options",
+        label: "Use Ceph for Replication",
+        type: "boolean",
+        description:
+          "If performing Ceph-based replications from a source OpenStack, the Ceph configuration file and credentials for a user with read-only access to the Ceph pool used by Cinder backups/snapshots must be provided. Coriolis must be able to connect to the source OpenStack's Ceph RADOS cluster by being able to reach at least one Ceph- monitor host.For the easiest setup possible, simply using the same credentials used by the Cinder service(s) will work.",
+      };
+      fields.push(showCepthOptionsField);
+    }
+
+    const renderField = (field: any) => {
+      const disabled =
+        this.props.disabled ||
+        (this.useCurrentUser &&
+          field.name !== "name" &&
+          field.name !== "description" &&
+          field.name !== "openstack_use_current_user");
+      const required =
+        this.isFieldRequired(field) ||
+        (this.getApiVersion() > 2
+          ? field.name === "user_domain" || field.name === "project_domain"
+          : false);
+      const isPassword =
+        Boolean(
+          configLoader.config.passwordFields.find(fn => field.name === fn)
+        ) || field.name.indexOf("password") > -1;
+      const value =
+        field.name === "show_ceph_options"
+          ? this.state.showCephOptions || this.hasCephOptionsSet
+          : this.getFieldValue(field);
+      const onChange = (v: boolean) => {
+        if (field.name === "show_ceph_options") {
+          this.handleShowCepthOptionsChange(v);
+        } else {
+          this.props.handleFieldChange(field, v);
+        }
+      };
+
+      return (
+        <FieldStyled
+          {...field}
+          label={field.title || LabelDictionary.get(field.name)}
+          required={required}
+          password={isPassword}
+          width={ThemeProps.inputSizes.large.width}
+          disabled={disabled}
+          highlight={
+            this.props.invalidFields.findIndex(fn => fn === field.name) > -1
+          }
+          value={value}
+          onChange={onChange}
+          getFieldValue={fieldName =>
+            this.getFieldValue(
+              this.props.connectionInfoSchema.find(n => n.name === fieldName)
+            )
+          }
+          onFieldChange={(fieldName, fieldValue) => {
+            this.props.handleFieldChange(
+              this.props.connectionInfoSchema.find(n => n.name === fieldName),
+              fieldValue
+            );
+          }}
+        />
+      );
+    };
+
+    let lastField: JSX.Element | null = null;
+    const nonCephFields = fields.filter(f => f.name !== "ceph_options");
+    nonCephFields.forEach((field, i) => {
+      const currentField = renderField(field);
+      if (i % 2 !== 0) {
+        rows.push(
+          <Row key={field.name}>
+            {lastField}
+            {currentField}
+          </Row>
+        );
+      } else if (i === nonCephFields.length - 1) {
+        rows.push(<Row key={field.name}>{currentField}</Row>);
+      }
+      lastField = currentField;
+    });
+
+    const cephOptionsRows: JSX.Element[] = [];
+    const cephOptionsField = fields.find(f => f.name === "ceph_options");
+    let cephOptions = null;
+    const properties = cephOptionsField && cephOptionsField.properties;
+
+    if (properties) {
+      let i = 0;
+      properties.forEach((field, fieldIndex) => {
+        if (
+          field.name === "ceph_options/ceph_conf_file" ||
+          field.name === "ceph_options/ceph_keyring_file"
+        ) {
+          // eslint-disable-next-line no-param-reassign
+          field.useTextArea = true;
+        }
+
+        const currentField = renderField(field);
+
+        const pushRow = (field1: React.ReactNode, field2?: React.ReactNode) => {
+          cephOptionsRows.push(
+            <Row key={field.name}>
+              {field1}
+              {field2}
+            </Row>
+          );
+        };
+        if (field.useTextArea) {
+          pushRow(currentField);
+          i -= 1;
+        } else if (i % 2 !== 0) {
+          pushRow(lastField, currentField);
+        } else if (fieldIndex === properties.length - 1) {
+          pushRow(currentField);
+          if (field.useTextArea) {
+            i -= 1;
+          }
+        } else {
+          lastField = currentField;
+        }
+        i += 1;
+      });
+
+      cephOptions = (
+        <Group>
+          <GroupName>
+            <GroupNameBar />
+            <GroupNameText>Ceph Options</GroupNameText>
+            <GroupNameBar />
+          </GroupName>
+          <GroupFields>{cephOptionsRows}</GroupFields>
+        </Group>
+      );
+    }
+
+    return (
+      <Fields
+        ref={(ref: HTMLElement) => {
+          this.props.scrollableRef(ref);
+        }}
+      >
+        <Group>
+          <GroupFields>{rows}</GroupFields>
+        </Group>
+        {cephOptions}
+      </Fields>
+    );
+  }
+
+  render() {
+    return (
+      <Wrapper>
+        {this.renderSimpleAdvancedToggle()}
+        {this.renderFields()}
+      </Wrapper>
+    );
+  }
+}
+
+export default ContentPlugin;

+ 23 - 0
src/plugins/vhi/MinionPoolSchemaPlugin.ts

@@ -0,0 +1,23 @@
+import MinionPoolSchemaPluginBase from "@src/plugins/default/MinionPoolSchemaPlugin";
+import { Field } from "@src/@types/Field";
+import DomUtils from "@src/utils/DomUtils";
+
+export default class MinionPoolSchemaPlugin extends MinionPoolSchemaPluginBase {
+  override getMinionPoolToOptionsQuery(envData: any) {
+    return `?env=${DomUtils.encodeToBase64Url({
+      ...envData,
+      list_all_destination_networks: true,
+    })}`;
+  }
+
+  override minionPoolTransformOptionsFields(fields: Field[]) {
+    // Remove this field, as all networks are always listed
+    fields = fields.filter(f => f.name !== "list_all_destination_networks");
+    return fields;
+  }
+
+  override getMinionPoolEnv(schema: Field[], data: any) {
+    const payload: any = super.getMinionPoolEnv(schema, data);
+    return { ...payload, list_all_destination_networks: true };
+  }
+}

+ 102 - 0
src/plugins/vhi/OptionsSchemaPlugin.ts

@@ -0,0 +1,102 @@
+/*
+Copyright (C) 2019  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import { Field } from "@src/@types/Field";
+import type { OptionValues } from "@src/@types/Endpoint";
+import type { SchemaProperties, SchemaDefinitions } from "@src/@types/Schema";
+import OptionsSchemaPluginBase, {
+  defaultFillFieldValues,
+  defaultFillMigrationImageMapValues,
+  removeExportImageFieldValues,
+} from "../default/OptionsSchemaPlugin";
+
+export default class OptionsSchemaParser extends OptionsSchemaPluginBase {
+  override parseSchemaToFields(opts: {
+    schema: SchemaProperties;
+    schemaDefinitions?: SchemaDefinitions | null | undefined;
+    dictionaryKey?: string;
+    requiresWindowsImage?: boolean;
+  }) {
+    const fields: Field[] = super.parseSchemaToFields(opts);
+    const exportImage = fields
+      .find(f => f.name === "coriolis_backups_options")
+      ?.properties?.find(p => p.name === "export_image");
+    if (exportImage) {
+      exportImage.required = true;
+    }
+
+    const exportMechField = fields.find(
+      f => f.name === "replica_export_mechanism"
+    );
+    if (!exportMechField) {
+      return fields;
+    }
+    exportMechField.subFields = [];
+    exportMechField.enum!.forEach((exportType: any) => {
+      const exportTypeFieldIdx = fields.findIndex(
+        f => f.name === `${exportType}_options`
+      );
+      if (exportTypeFieldIdx === -1) {
+        return;
+      }
+      const subField = fields[exportTypeFieldIdx];
+      if (subField.properties?.length) {
+        subField.properties = subField.properties.map((p: Field) => ({
+          ...p,
+          groupName: subField.name,
+        }));
+      }
+      exportMechField.subFields!.push(subField);
+      fields.splice(exportTypeFieldIdx, 1);
+    });
+    return fields;
+  }
+
+  override fillFieldValues(opts: {
+    field: Field;
+    options: OptionValues[];
+    requiresWindowsImage: boolean;
+  }) {
+    const { field, options, requiresWindowsImage } = opts;
+    if (field.name === "replica_export_mechanism" && field.subFields) {
+      field.subFields.forEach(sf => {
+        if (sf.properties) {
+          sf.properties.forEach(f => {
+            super.fillFieldValues({
+              field: f,
+              options,
+              customFieldName: f.name.split("/")[1],
+              requiresWindowsImage,
+            });
+            removeExportImageFieldValues(f);
+          });
+        }
+      });
+    }
+    const option = options.find(f => f.name === field.name);
+    if (!option) {
+      return;
+    }
+    if (
+      !defaultFillMigrationImageMapValues({
+        field,
+        option,
+        migrationImageMapFieldName: this.migrationImageMapFieldName,
+        requiresWindowsImage,
+      })
+    ) {
+      defaultFillFieldValues(field, option);
+    }
+  }
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików