From bedf1ab6f38a80f5a936083aac9d20c4e7ea99e5 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Thu, 23 Sep 2021 19:10:24 +0200 Subject: [PATCH] F #5422: Add clone VM template form (#1477) --- .../src/client/assets/images/logos/alpine.png | Bin 0 -> 1739 bytes .../src/client/assets/images/logos/alt.png | Bin 0 -> 3038 bytes .../src/client/assets/images/logos/arch.png | Bin 0 -> 1820 bytes .../src/client/assets/images/logos/centos.png | Bin 0 -> 2202 bytes .../src/client/assets/images/logos/debian.png | Bin 0 -> 1702 bytes .../src/client/assets/images/logos/devuan.png | Bin 0 -> 1075 bytes .../src/client/assets/images/logos/fedora.png | Bin 0 -> 2143 bytes .../client/assets/images/logos/freebsd.png | Bin 0 -> 2344 bytes .../assets/images/logos/hardenedbsd.png | Bin 0 -> 7652 bytes .../client/assets/images/logos/knoppix.png | Bin 0 -> 6649 bytes .../src/client/assets/images/logos/linux.png | Bin 0 -> 6252 bytes .../src/client/assets/images/logos/oracle.png | Bin 0 -> 6697 bytes .../src/client/assets/images/logos/redhat.png | Bin 0 -> 2822 bytes .../src/client/assets/images/logos/suse.png | Bin 0 -> 1022 bytes .../src/client/assets/images/logos/ubuntu.png | Bin 0 -> 1876 bytes .../client/assets/images/logos/windows8.png | Bin 0 -> 3319 bytes .../client/assets/images/logos/windowsxp.png | Bin 0 -> 7276 bytes .../Forms/Vm/AttachDiskForm/CommonFields.js | 3 +- .../ImageSteps/AdvancedOptions/index.js | 33 +-- .../ImageSteps/ImagesTable/index.js | 73 +++---- .../ImageSteps/ImagesTable/schema.js | 3 +- .../Vm/AttachDiskForm/ImageSteps/index.js | 49 ++++- .../VolatileSteps/AdvancedOptions/index.js | 31 +-- .../VolatileSteps/BasicConfiguration/index.js | 31 +-- .../Vm/AttachDiskForm/VolatileSteps/index.js | 26 ++- .../Steps/NetworksTable/schema.js | 3 +- .../Forms/Vm/AttachNicForm/Steps/index.js | 43 ++-- .../Forms/VmTemplate/CloneForm/index.js | 21 ++ .../Forms/VmTemplate/CloneForm/schema.js | 59 ++++++ .../Steps/BasicConfiguration/index.js | 12 -- .../Steps/BasicConfiguration/schema.js | 7 +- .../Steps/ExtraConfiguration/booting.js | 68 +++--- .../Steps/ExtraConfiguration/index.js | 5 + .../Steps/ExtraConfiguration/networking.js | 47 ++--- .../Steps/ExtraConfiguration/schema.js | 28 ++- .../Steps/ExtraConfiguration/storage.js | 194 ++++++++++++++++++ .../Steps/VmTemplatesTable/index.js | 9 + .../VmTemplate/InstantiateForm/Steps/index.js | 7 +- .../components/Forms/VmTemplate/index.js | 2 + .../src/client/components/Icons/opennebula.js | 42 ++-- .../src/client/components/Image/index.js | 7 +- .../components/Tables/Enhanced/index.js | 24 ++- .../components/Tables/Enhanced/styles.js | 5 +- .../components/Tables/VmTemplates/actions.js | 54 ++++- .../components/Tables/VmTemplates/columns.js | 6 + .../components/Tables/VmTemplates/row.js | 25 ++- .../src/client/components/Tables/styles.js | 26 ++- .../components/Tabs/Vm/Storage/index.js | 7 +- src/fireedge/src/client/constants/image.js | 62 ++++-- src/fireedge/src/client/constants/index.js | 1 + src/fireedge/src/client/features/One/utils.js | 2 +- .../client/features/One/vmTemplate/actions.js | 2 +- src/fireedge/src/client/hooks/useListForm.js | 4 +- src/fireedge/src/client/models/Image.js | 6 +- src/fireedge/src/client/utils/schema.js | 9 +- 55 files changed, 764 insertions(+), 272 deletions(-) create mode 100644 src/fireedge/src/client/assets/images/logos/alpine.png create mode 100644 src/fireedge/src/client/assets/images/logos/alt.png create mode 100644 src/fireedge/src/client/assets/images/logos/arch.png create mode 100644 src/fireedge/src/client/assets/images/logos/centos.png create mode 100644 src/fireedge/src/client/assets/images/logos/debian.png create mode 100644 src/fireedge/src/client/assets/images/logos/devuan.png create mode 100644 src/fireedge/src/client/assets/images/logos/fedora.png create mode 100644 src/fireedge/src/client/assets/images/logos/freebsd.png create mode 100644 src/fireedge/src/client/assets/images/logos/hardenedbsd.png create mode 100644 src/fireedge/src/client/assets/images/logos/knoppix.png create mode 100644 src/fireedge/src/client/assets/images/logos/linux.png create mode 100644 src/fireedge/src/client/assets/images/logos/oracle.png create mode 100644 src/fireedge/src/client/assets/images/logos/redhat.png create mode 100644 src/fireedge/src/client/assets/images/logos/suse.png create mode 100644 src/fireedge/src/client/assets/images/logos/ubuntu.png create mode 100644 src/fireedge/src/client/assets/images/logos/windows8.png create mode 100644 src/fireedge/src/client/assets/images/logos/windowsxp.png create mode 100644 src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js create mode 100644 src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js create mode 100644 src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js diff --git a/src/fireedge/src/client/assets/images/logos/alpine.png b/src/fireedge/src/client/assets/images/logos/alpine.png new file mode 100644 index 0000000000000000000000000000000000000000..66592c3660173a0e2ebddc65ecea0e87a8b86725 GIT binary patch literal 1739 zcmV;+1~mDJP)Px#El^BUMMrQ<1XzFrSbzjrfCE^716Y3qSbzjrfCN~8 z1XzFsSbzjrfCN~81XzFsSb+4CffxV)01k9gPE!E$3CtrDG=$-Cvs|4=Orr33000Ie zNklSp%nbt#KnU4aoUWdXu z3Ri#4T2z{0oq zHb|miaSgHWuP9h#jc156S|luk=M}H@NLc1kY#XFwxg6I9=@{@&vZ6)AG8TMTJ8DEM zJnXZoB>EUG$F)JBA%3dHz{0;Xx5nor&iG+4 zzDIDq;;b4A3*UsHQcNrmJSFByypbP?7Fh;In@UBGiDfQ0lT_-WR#F%$ z<@bBsl(chMHkhAHz6xiE$!~?U2GMknq2gQZNzMp9j`Op=M-3KD zA$=TZgND7)$~1F7H8%qdQDlQSKj(XG;lg9lK7C_P@>DJGu40H88$+TvbopO9RLTX> zvJNXHh6{73C?J-)CAL9h(K%w%?i!^w0%8Tr5c>okk#}#sj2oC0?EjT?kr>h2Iia?k zQQCB94F(ug>LIGAqF=IZDyeSaaR^Uxu$SjQF%Xp-Z-|;;qqcfeaU`x$ckjnhpAw zfEW)8QgDdP=SR8*kvy!_x<52N`5R&-YQ17@k8+;RR~m3Ls(No&1MY^Qp-K1KC)eOF za8DiwYulZTGO;@-AU3Y|f(2-0?$Q&UtX>=GLJawD6iTb48T2fjyV8IH<`DB?HR(4p zmO_{3Xryoz660tLiM6HJ77B^oW{GLtuzJe_OhBumtvy!R=*cZ)prTiYtEPtq?f0d~a}um<}~Io`YD;ON`{p-1j`xm_>~4 zyA$&x&+$?$ASG~GFz4h7Tm$$IC_ZI>;~xD<6fgV2#^mRvVJ!*XFj2;R(c3D*6cvhr+jL z_`KC@+*Bc(G@Oz%Ny9FDoU2K}YULD^CsMFlxsEugXkSu#=BWg%&V?b&S%n#$oE3Tz zf0Ax-p`^uEM5Al&vG?UFcg>0RJViGrDl=CrGlwiSw=Fa0F0&ROwMLPYSUZtf(~(+h zl34?jS(}quGn84&lv!hyTKkP$ljbz(M6H!$POV&K4PR<+fy|xINbNzA*&7wUXNw4Xw!-(48PDEGk6YN@I}*@)C$lF|YOi8QLtHMuq25j;vFB7~ hFRaua-B`WD{{vdK9+V1khvonP002ovPDHLkV1h@_GI{_2 literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/alt.png b/src/fireedge/src/client/assets/images/logos/alt.png new file mode 100644 index 0000000000000000000000000000000000000000..647fe2f1c4aa7452da7cfb690cf9e878db657e25 GIT binary patch literal 3038 zcmV<43nBE0P)Px#oKQ?uMMrQ<00030$^Zic0{_YYA|V|S5fS~!0RG7U z{>cCY1Ox*E0|W#F3JMAU0095W009930RaIH3VXAeHpu>5Hy#axQ%@Xx#ei~UH$&M%g{fL%c< zr*JCF7rcZrpSlCxio(7BupN{3HDcO@07;s#f3Z((( z7%BQUhqMdx)HE&SQz0G=!MAwFoxxpC#qurVlSUbQBqUgf!q^_tlwdh&XhCWsO)xzC zOXNr)LQeUfij~4kZM;E&P2|Y-Ou9b^D;zf257H2T=|YYKBBX>bQh5dMhL5m3GY_Ub zgKyfi+#EyF86G>+;QH%L-fl!u+#I*EEP=gAk7I=lmZ6w~8u;T8z-xv*&lD;xVE+~m zf?oE0Sqi&@rv9yMRQX5VvIDY-!;S^1HHD2~hw0?Na0tm@v>bcOhQx8QDt6tfDXOX} zGF=e14`bz@LlmDC;xz|ieu`#%8roiHo@po&5lQXI(93K{jjlR@6?NZNb&p=TmmbD) zLfcO1Cz8-du|g6M9hi`rW@;j|Gxre6OD{xcCw3M*-^Pflak@5(h-=lK$Y~G^`{Pj& z1$}TR7G1I+83O7*`b&7Wbw=l601AU7woCxZ1)#ER>6^tTs60e2&AK2D#!RH*>EvV$ z={_Na=pyJjY$AHf0j#89-*{JO@mS7~XAYz!LyF}d3BTvFpJbvR44v7TNFJ(ARo$@< ztB>oJZxMI+Caw+A!F6OA<-Rougl`gw3a8^c$5jSOkk*a+o(0~^k6?jvB2#QLKc7>N ztSMo7hOa=*$#-oc5by-7n%+8*g*;6P;lY?@sH0SK#pq= zhls_}q-S6mgFS?0>CGHVe0u_xZ4PG79pNp&GSMFL5&sOVp;!j93?A2C(B3NN45;Gh zzk_AiX0W8|HY{}R#097D+KaFV)B-Fx!z1LZtYWba@=LHN)Dmx*9g3{DEBYr3#3j~L zD&N4Ggw^VCndP=w{iEPogynyYWt*N&3N7zkoUlbs_9Nk1fTetgWjU!I8V~01XZ9aXD{= zInGq4>FTa6+*91@wae-0!kGWq>lYSSH?Zb*fo0N~xs3`a+%w?n^(^V{u1!C7`W5rZ zSmS=Iv6OajkrkcL4)Qv`1WP{XSB#&i3z1w4=Iq8E~AYJU3FoSH&oRW3df{>TB!%i5ED$uRzlkqOj zu);i_x*a5%XY=Y2%}g{=bqc~2HTMaGB+FYQTB3xorfCozYR%+SQKs(D#68E=W0Bd% z7Gxg6zGss0c(2NPVd{U0^=W)}_z;zt4(HA#?rNfkT53t&ZdA5UzK*3V!BWmmQgT=c z971=WiPCl>Y6oGasP;n@1lhxCY%2&BxT_d~e&G;lEAE9W!u7bf>;PDXbC6;sa7^AK z@{-7@yyO4WTPP2q&~oW4q$gOX%qhw{jzmUIg+p$7;SarKc)5-$_*2beB~F$tPsERL z|90Z`k9TIQ{h+r%OEa2$1S?JkcQwx9MAXKC#dapAk9f;K2w7P z3jXHAa4l0)Z7%Mk$H>L+1r|SG&S?UItK8FdEZynGZ#Ct@DYMKs7`bP%`*UqH!nq4y zY=EwJoU2Z=dFkk`Q`PO36%g`t`UvPge)I;a1FsH}OrN_-KsRM|JfoUIo@nZXTOSq{ zHu6zaHBA+d$9HsBXN4jD&q6hxJ$}VZ13eQDzEP{|DTpd_=Tu3?RrK}rfE(?%kIW$W zY3a4usey;#Xz*>|+s^2Mswih68?oQ)j}KriG+Hl1TwVIAaP+#Us;0dbUJzitD8CAe z!kMI6=XbEwqXezG&9s#n6Mjj%KT0pef|Mwei)gR)C~6YGaM_yzHd*6L&_1MBVWAt& zXijyi^fpf7y3FFNa@(u<2!8AZZ6m)3E7Z#iX@J_HI8`m`#iEwIDsZr0hb@nzPT#2; zS$e@HLcCvdmg_cX@=(PEi-rAKbYt+lRn(R{P25UP#)9Ijn29@ztmfn0y0fbXokyF>9P4gUM986nc-vvpzwWfZ`Rt0yteq)34Xr+^xP=b%!;O<7>qcQy z@4nEx-8Zq`N`^3EiwW9jw24bYZ;Z8)zrTLuj_P(hH^vroHnZVY3{x(edvSTI?7Z&} zT)wV71e#V=?abt9*w~CcsVjbnIy3cYSUX&@#9S4_C#TzV(?K0Iq-=4t%lw9#v@eS3pQc>PteLeJFH9aa_m+%xzob?0^K6{mrSS zsh1>VhLn)Va=KTCymZva{Nl=hvmeJHQG#b8NJXHMb_%!Xzu@|@&P$uoQyPD1)OSdl zXt_|~1G+(bi1IbUWjrtK+*jY0UW@PggrmyspXKDWIi5#!X>HVRz`o8EJ>48 zsE6HdchE#p-J=UPM;8w2Mx8E3$_^D*2l2QORao9_AQCiqQrIzFnS4}{Y5PYqQj%|6 zw2{_IlDEgrX7eGFOB;lV4VaJgC*`e7pZwU)`!VU}Cp_2C#UPk0OK+>Kw%Tf|t+v`~ gtF5-$YX6A+2VyBAA*D@Y9smFU07*qoM6N<$g8KE(rvLx| literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/arch.png b/src/fireedge/src/client/assets/images/logos/arch.png new file mode 100644 index 0000000000000000000000000000000000000000..11a3aa64306bccca60db8e27fdf6486f93315c6b GIT binary patch literal 1820 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 Xe%P1&&`)#iJ*zo6x;Y5#eUknKwHR*Q literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/debian.png b/src/fireedge/src/client/assets/images/logos/debian.png new file mode 100644 index 0000000000000000000000000000000000000000..59d85ce0b7589b6cefba8eed4fc7b920ee06e30b GIT binary patch literal 1702 zcmV;X23h%uP)Px#El^BUMMrQlB{cm=_zIFn8&HdF zNqs1_+SC%H8iJ&zG&V-C8?!QRu8-;J0wcTn7G zGI;4oMR6*0FMpMV|H=mUWwD=`0we5wY&=eMFg=}>@VQmLvTMeRPg&qo8OQt+1h2|s z=~wX}LvSWNTE=bJv@_srwIMh@-&(=;N0RDruVL}`&XJg+z1^LDd5BTG2h#7?XfLm( zmA=Tzz8KqOhFVD$I3xNIQ|sn#IamHs^gff4+v$+`J!!pd{uX1o%cd}`_pFtAB(x>q z|ws zxb*16c((+qr_md+L~>g+J4YcrK&iC-_;J&pzXz7G=yF2d$kgpU z{e{#ZFJO`k}nPdAWxE+lzl zGkG^XX%_Xkn83t^>%e3&Or=;oODzAoi(+ySo!kl7%3&c&9|*-2cVL?;#5TIt34vWHxEcj~xUWfF)0Xbxu z;oK19ah!$EpuQ(F8P*g8;5F#%e<3(El%X)3lzM;z3AlmXu9js$e;_+O0TvkB+$jMt z_|_XZgjk_cI|Tq9hLV$vW*gSxhf4wYXJ|9X8lgBt21&oq87xLYvRC>t1h^*o0T_^w z5YsHoB6I~aoo*QUL8uc>L_L~LCygVf#6kS z3UUL9QVwuA%=S!jHXso>I}Cf(aJNugBAZ;%{K&|1W~fkU%%GBO(As@r0M-N zo!bK1vgyfCn7+;x*n8EMu88u!79YzzsKGrvNoxdT(12TqQa6Zqio};`Gpb{(HGs?t zXKfEB2XJT-5nQ}N)m<`p+?N={WJ@uUm$l>$;ES1wgiG*R^GDcYbP;{YLuEJTBvexAD|Q^< z$@mzdIMZ`w=Dpft=j%9q1XM?9y{Ua+dV1b@>mkFZM(=HcueKuh%0qLwt3!%XJJ`Vo z)>orHZt;OV#AA(qKCnLIBzbRF_qp84m^r51;{=o4f!?0n8okB~euc43x{vfl-fQr1 z9v;(g9M1BB3-d8rzlrG2M{jPU3-TywBoI0o#{%| zm4z-*mT>o37RV#i0v4Sd=S9C5$Vc&0PFL7DFTBVt(Eu;{EpqFs@X{z|l!TIp&*F33 znR1+5QwzM5f~T+b?jUb;M1& literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/devuan.png b/src/fireedge/src/client/assets/images/logos/devuan.png new file mode 100644 index 0000000000000000000000000000000000000000..8df52556fd46f1bce945740dbbd922e693233aae GIT binary patch literal 1075 zcmV-31kC%1P)Px#Ay7RUR{x0fY&Ae3BI000A;Nkl@b_5bu~rrrv@a~P zr^*1;Bc3Yg6SUa$de!542Sjt9qpAnpa1E$0gkG_gzxrVQ;{5B&uSJ{q9f{lB%f~RHhth>Bn`z`G4a}b*%;<}8d8-!`2}6RUSQD2F;_#?+bG(6Ir~zB z;t&SD>nVAM1dQUfN@e%W>Xen*)_p&$pE6WA6FBCa_wCsDq(bTy>Lc*+H9rKgf| zuwc6;8jyAqOa^Pa(*Iwzv5z*}45S~#tP`Dh$~BQY2)EiLg7%i$3v{&_QRyZ67)Y>Y z4{;s6DnwB z1#~P?To5KOBTRxwp9n4sSq%4xz>hvxiiUfe{)lrWVeq(9$jnh9_MfV)4Ck1ON7)S? t5&E{8AsMpF2;9KHz`(%3z`(#Dz<)}zOJX-mUu^&Y002ovPDHLkV1hVM{+R#( literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/fedora.png b/src/fireedge/src/client/assets/images/logos/fedora.png new file mode 100644 index 0000000000000000000000000000000000000000..476e2255d83b8d4f79e081d55dbc6d8cf29a4e59 GIT binary patch literal 2143 zcmV-l2%z_gP)Px#S5Qn;MMrQj+(@%tQKHT^<2z3-I@9NuLUTU%f-&pB#FWxh+{Nc9LGTzB}sbu zGfR^wh|%qeIlJCa97KP>mL_2=0@}jG;hAV@6d(=KVkn5xlbCM!klp-~B>}uw?1Rhp|Rs+kQTgzG5(tUs>rv*E~P02C6t$VdKwF>+Q8tkMQx~I(YAuG0E z$GD(Wq%Y-A6W1#n!8VmOIzg+7J=NBYTv^+L^a4gZp+o-E*)U>l1v8C%C{@FrdSlsa z>!dW}N@4YiRGk6Uv&(ocl=`dfW>gDSR`iGJec)5871o9;^pk|fRNvT|xmJFRe1tn` zOsS^Awb&}Y)L{<{EXtn8#hSPb!z+Y^`(R4dy*5}`Z4_P!th!gZp$FNHY9031#Bs&y zr7q0JiPmJ}ylXTLYU@U6^#Z!Elxb5o6u+mXv}rT3>D>r(p(G3Qq1jH9Xu6P!L&pDb0>TEf*j45q4EcAc4e`lpsSCt+tpRSo1JzQO)9<*L z-cD9aJ@|Zs3mjTdwUk5k5?tV_1yoBtu~{ltfX`J+Jq=tcSA4Eo>Phnw^YCH{Q%k+X zSSB|XTTT$Q>P+v$sW%8uM2K3N}Ce5tb|HzxE7eh(JGR2~_@9Vm5s&Z?B#zbpTLwHiaP)?xa}pHJ6Z zEe2rGRY(+2t+@Rs@m7>E)-Iukr-iZ1yf&z@_QOPHgC&$p7Vavn{Xh?otl3`2s3Zk~ zA0{lZh9e%7VF|{0*I2E(;UvH!0xGLSQ}+ zO{0IJ*7taZ49g(2e5mz3%wmD%L9OK+-v6c6a!~63QfoOfVyD*AE!OXVjWiQYuu8{R zzwcCet$mL$uZWY#Qj}AJwI+AEGjE8CG_)HN`Av+jcTuy}V<%_TrfA!u-d%O$pN{1v^Gt`AV%m0wD%Lgfs-eG{{cJS VvAO|$J%<1Q002ovPDHLkV1kii7>fV^ literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/freebsd.png b/src/fireedge/src/client/assets/images/logos/freebsd.png new file mode 100644 index 0000000000000000000000000000000000000000..9767cecbf3e1c3c9e738773775bb97882c5eeccd GIT binary patch literal 2344 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 literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/redhat.png b/src/fireedge/src/client/assets/images/logos/redhat.png new file mode 100644 index 0000000000000000000000000000000000000000..63d0bae718e87b703ddc69476fd38545c0caf74f GIT binary patch literal 2822 zcmV+h3;FbkP)Px#qEJj!MMrQ<8X6h^000000Q~&??Ck6S000960{{R3 z{{H>|0073u#@pN5|Ns9000000000000001nhK5T^OaK4>va+%|Iy(OT{{R2~oSd9q zUS26FDF6TfaBy(@`}_a@|Nj2||NsB~{{H{}|NHy<{{H^||Ns8}{r~^~|NsBZ007Vc z02%-Q#{d9E0052v0I~o8F8}~u006xJ0HXi^cK`s)3Ih@V001a-QchC<`REMt`jPlL z`?mP`je6G{WcK+q_xB0g_VoA5_Z7Kjcty;z9$g?79ESJ+013`XL_t(|+Qppdf}*+- zfKfq2a079-)n4X+>b>aAq}@*>KbmRNrbP{SLrdLv;Z*=K*yGqM9Y{-s4HkW?6Y00{u=%iwSGj&1r)eDf z5y``vFG;)svnRs`yKmn}lbd&SoU3#7Zb|C%H{=Kx7W^Q4vU7z`4jyo>8^uBB!OPeC zcfp@tf(M&5;=;LAlw7kX-vvD1yMn9%ic*P(P$H6kZd07ml=gQcszZ&PXj^2Id>D(M3NH|xA zv8JFqy){Dx)gi6XD&}bzB~p)_h@P;spjzMGpB|ss~*l#CW4tiv2(Ll@)JRhP&ofA!hCh}DWiT;d}yY|vK#v};{9pV5kg1H(uLvw}cF zpC(})i#oJ`Y8?je>kyYzCLiANY#s~)0o(6G5OF(0b?pt)O z`<#?*CBo!-OJdyp#_##iB~&jV#apQz>|^a@CIMuDkI#$c5KpXLZYiw^p~nZm@MDgL z1k0RjQK#5Te5Ov4JRm82a`bM(0#TQyuhNxbe>>(J%lXFC|^+~K&lzWr*mne0$=SKAzua7FDMa$mW8jXm^V9cUE;VmiSXlgUHlOz}<>Bvb0eZRw;Le!kBg25Jv`d z=9a75_OhE3H)*&D`Q?hfp||33)yBZ(_hQWQx&%6C(8cYee)XiVQHFeEz@#q8yi)sSl( za}b^4khT$Q^)&k-YVR>^NI2RO)xS=uo16NR%2rgF%lx!_;tRduN!eDBRlC5Y7pLUS z=ck31C&p~CP&Qo)HUuIXrGU}W%bY9b*dS($yz2MuQIlJ|W^k;@SjJNsJZsbNDw`1+OZ0*T7@l2*@J5Unsx?^W*F zTkUp6esRCkcdHymHnu9uhj?yt3KJVEj~WZ-_qL+xtDc-mZ)F^}sw66kqHR}l&oH5m zB~>@}dqY=6Tk6%Yp&)#V1g>0PvWEQ;QB$F4Qo#WfNz=sF7gF|bwT=x1VSU15+0qth zqqpBGHc8&?j}V;!t9TUeOnjgSKDlJK7iq;DOYicxp{jy|s0=Hyd=PJ9cQ1-w%i*Cc z#qP001{+q-m3=^6(!Q>;c&bZzg@p=MueP$dsY~4%JM+%7HOgF?Tw7T{T^lY`nMMp1 zM3rr|J)vWvjx}>pm;T6qy788;OscX_hrs{);echz_@+s(7qgzBoLeWf9Q%DIB6gs9VWi=2GNzzPx zhkYC4(qS;&7MPBv^zJYh?7J9Wr_QLwXX0B?>uyqZF`VsH#1Bl?4KB9ji0cY)=EwqtkCv|jl13nFT z;ayQE;^j+dFUw!99;W;*1un8a|L~*wY3Ws^`NV{i>u)CiasBOX%LxY;6DLc7x3mW9 z0t&iV8&fKqZlLb%+WFK-W#Z}l*XeJiTz^k~YQ_uYXY#!kI+rg%#;h|DFntLhlCS0E zcSA)JH@}zV$C#dI=gDa!D(L*OIZe(VM|yhN-}2vO^82&di1)#sxY_*tPA>nJf9<^K Ye^K4qY~m-oq5uE@07*qoM6N<$f+$5|EdT%j literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/suse.png b/src/fireedge/src/client/assets/images/logos/suse.png new file mode 100644 index 0000000000000000000000000000000000000000..8130fbe09786bcb553778629c94cd53dde509ab8 GIT binary patch literal 1022 zcmVPx#9#BkFMMrQA^h0RET$VG?JN`lHp ze#1l*q}lTT000SeQchC<9`S%aqTXJ@>Dqx9000ANNklpL%hyKCh>!PNHZt3)79XccGzNnfZA^^J3%!JHk=m+gOb#krF@sSO&O(?)pzi#H!*6*6 zcn+IrglR@ z+AY!IcZ3t~(_U9{J#BecR|@rO{MbvD&b%k1h%Ne9h{sF&3Non+IOxG^v*L#7R~gKQ zYQ@-d1TXTlHsAWZqW?YeneQ||E6!(Y{dRVrfAn5c@^T^i&2zI1XGcCxfBN!rA+|F0 zwBw^Ka-%SP?1_DV^FMv4u|wj?QZ}2-75sEw9a0fm_L4OA-CfBpbKUc4qWYa2?0kn! zfT*w8bCt7{262Y5wO}GKF!4>sF=zFy%#39SEs0+S!9*5>35a7W*9Gg;v`c^;)!Ske z3NCW2LF+z|bJ4&qW)H3d=CP7v`14|3yB51I_bcOWY4v%C^rgsxj3b^p)O2U-92`FY zL$od9nTs906XCn}8a#yS+$W7LBz#R6=VqOGgdgE^_;n}aM0yw8&=)J>W?e&1&E+AV z47nflmJF!A77(9j{ZX>(p4$qK)F8+(>dAd)db)Px#PEbr#MMrQ<-bM`GMhxCY4Bkcz-bM`GMhxCY4Bkcz z-bM`GMhxCY4FCWC-b4!CLkIr&_TW++=zm!I>g3{HEb+9S`Q6gxZ9(Z+ zEoFZI000YgQchC<#<5_WfZjCl98p`wMa=*J28~HXK~#9!&0N{Gs~`-$)mjG(BBJR3 z|L6tV(^f)MQ~ymk%ZuCJHEj;zN} z{t89~Ro%2$>%XjgJ332QTk1j<{yq&Aad6wLU{Ni*P+^RZd9dh=S2h$@9I!ra=cg<} z0Q{1V(*(ALRqM%RT#oK^7k~hfZfIv2IvHr@NaF-+pZ2cA5&^GkC&qi)G=fRu5A8V{ z+iuHE#+r=l|f7>^fYk|q^I-*prfmQ^S$gVhzSQq z>usT8$*sw?Z9{3s>i8-I($~8(f=D0K?{ETpRY!T;y~DT^?I);z;3@ zNtx6oztw&lx*xz$4CVfO7nz-82;iyNh4tjo@Lm;^8FZy*eO}n9N@RAkF@SJ`8D*fs zq-8y&l#c^4qC!RPtE^c^BW#3X1(%3KAS#Tm>j+@063t6lKv?s){DrW6ST&u93_}0_ zmJ4a6uDczog_-v4I}ynlBjBvA%n8*T zT;!sBt4qXa+6!(sjV3?}k^i>Bl{v;}Sn6hG{nMv5J)2cRh}6o+`mLqFEbO?5*s1Lr zTm-#21oh(}Gc1|^%^VUdZy5E+n$hBCz7rgC3ASjnB!kWow#+$Vb zHR(cQ@!t3q6x_`CwxZdFOtYxKCa{lnm zs|{St(xX<+%3`*tbZgkGuF8&D%&AgXg;MUSuFQs7t9sA;y8Fterp1_8QEO7(b^_!i zc=Z5mK`xWmQjOqbLaiA&vG81}$p(#mWo#GbjVwsbj8`{aOs{IAaFZ3YO~!x90)A9k zB5W(_;%0}c*?ADzipVto`ZUY<(YrEBw_{;ii?>p<5M{~9d)o84Pou=O(F2uyHz$>g zrMOy%@KvD8KFz{UHv{%TrEZcK$R(CyRj6KuDoNFMTH|HIwH4X^Q;JIU59)hKFZJaQ z8S)h7#Q9Y554y1Y{jMYSL#4PGlhQNQZ86;r|NEgn?m_2Ksc{2tx%r^khGkz5^NR}Q49-cjro_OZOAO>*Vq8#=A&-LW zg7Ovnl&_H%`-ZI%_|evgX(I_A-ADqjjU?la?r*l;V%?-XbxRj{Z|R=90Y3U}fScQP zKWP_ObcBD>Trswy@K0J%kv8sZ`ueVIPPEbHL=J7v;7wr&(iFC8p;HZYv95&3FUH+l<35`$vA`u#pZA zyB&DAZq$0c)wP>Q8wb--n89?9F@Ac*7=PG94>;JN2iGGac<+b^ayueYopYdOIPpM@ z_ZdKukv_X7A773G9zTlouvV-`+rk|LgrEljV?8z*?2svhJ!Bf`k=%fXy@MWL4u%WD z9`}y*aD1#+3B-EwLaf(R#CoYmv{#_SdSOhg*Xzh~=#0yV0^HsdTw%pM%w1PeUUU`h zwP3Mcq89CybJ1SV7wvV1(O&)-?NyoaUMw2#HLQQo;=8o=gYA?ne*XixMj@$8e?%z& O0000Px#T2M?>MMrQ<0IB5%u?>N&dXKDz52w(fY%>}tj6 zA++os&;KB}?=QFOExPVIx9KCb?J3RuM7Qb%yZA`V`w+400Iuo)tmp)_@Bpgj0kQ1| zx%3Ub`4hwb8Oi^NJix^Y+u~gs|Ja+*-Zi zm#6*V?RvfL{(shX>y)Un&J?(ONqhDLyp9_t)3g`}^1TYZ&m0)+k|w zxM+%RLt*XvbsR?+R(DOG)XvGYH z`FZ??xO4!~fB|UU!^7`s;tOUB%t$b^9E9dSk$@QO&IKmK#L-2B-u;XL7<+d#zvLY# z+7OtQBL2Oz*=A5=t^xf_lNc2cm4V^GD(|A5nIRQaIz$4z?cAKDiA$U$RuuCMdAu(sF*NM7;nta?>FNf zt%;#P7=|ln2{aT0ISbsIk}xA?49vUT^>ls%O^!3X_B>M%W|~XhLO3X*ITAF~_z_dr ziSr%yV)iytlG#&42&WDM^PE$Lph_SKTF?>me7nQ%oW^l7o4K8dcM?%%&&c5F>kjN{$H%#j46fODpZQ}9!g;DN@GfDV4dJl<|2{8?uI-Ymvg(qzI8 zLKHmAGx1d*f&OKa<7}14ZSpYVRMce3C7P$Xq78=9B!03wo*(zldPDEC(>8YI31P9i z1QKw^SWqTrcQJ&Y=!quzxE^(2-Yr4b0fNPVjk4%ljFZU+vETBbiD6uo21q{7ceCda zlo+~bo`rgeYT^@WoTd!M*BC0##5E}(xlQ8$W;uo~D&s^Uvdp2LIN$i2hvEW;WK*Wi z(~+FVP5=gMo#+xT5n(1wGE(0}B8~-B;0$K6B;8<0L?FTbu_!cJN+ctK8VlJp$EZnY zkS5+gLlP@KSh_`$iJ)-8B$Y4HOhD3kk`DVQaZ$%>64KkJmcQ|CqF##ihIj`>?Ld-f zq=7sAizKniBm^UhV>X+zQXEO-Kxh&Vp~y`&nUX?0Qh8WM5l=zf1QLlUoG=3uE!xhx zCSD0no~h&|6fYCjM6<|Z+(woTS2nIejZ^lrnB1{@Pec>RxM)aOK$99t(!R5-;u|H6 zSeap@x|nSu-YG*QFRJN{NTierOUX$%PR13M#7)srk;KP|q!c8ft%KHSQb2MLOch3& z7)wOq5_;?ciTULrxw{35k<^~tPl+aglSd1Ii6jvODj#~~tt*UvQ<72hE!RX2L>lLu z=%Ep75+Gogq2_e>J=)5X_T+`Z5o;uYn?$PkZdBGJY~;bYEE6e*0v4x<3}#RUNvd2@ zamyqlojFOj;$$J(d|?s@6c;c9LGgJcfTRs1F{iq*vT?fLMiDg;NE81sNWQWl5;rh- zmN<2$a3iYGl*uJjp2b)i5-q?YQtzOh`#~n$w%CX$XVRuoYr;kibPfTf|TeEs9S z6LW|Lu3i8TO+>fV;sUcHJf1}9O%`|pN(Z7)O@=OM*Q;1MRB*sToG6w1XNZ9tbrF$7 z|EAcjhfPp1jy(&~M9H#`@Ze8KLQDDq3k_edi2^GnGl~|fNxDLER9m1Niq&Kolu%X{ zPMEG0Svp!?{uVO7uwr-?4Hph(F_xDlZ&?vNyIzk`<~4tsdvKNLjq-qD&6 ztH%o_agD$Y4_#O+(JlHdypbRUO{8#&$dWFDt8xOziGW2x=lc@UhsD8E=->z(M{?d# znPnV76oE;cCSHt09>O%_n&A1%X@Us6E(Irvj*{dPc%VZqJO(pi3^1Y)CqTrfQK|{c z!G{cvZ2E?=Op}m91y#gQ*CgdiS1dyzh(?yAp3x$WSmAE44d@*+M4%u0n%zXuq|bd* z$mmoPVI~T~BK#vfg&(~$d)N89E_n(hLN!g(TT{y3)e?!2$m%MZ9x?2y#}r(|3JDt% z7x1p#CDasB*(A!~BrC{Fc^~0+H#hr}njyK3V|v^bRbe!krPUrZ4|WqbZI8zT?ya34 zXKY7m7qfj{z^=Q?<@h*ecJcr9Q~yxA9_+c%{fB*VxpMY#{ixZS*{B^3yEpnj-23Ks zfJbb5eRJoF&G{f|`kfPRtv7pet4%sw)Qdh1_J$ zuAklYo!K7uirw_jIdnkZ1AG0VZ$clGad1+#4v0pLVPIxvb`9vygpPM}vc{0eKAJKJ z_Fgl^b}YgJB7sp{6YAxXz#ZF&Dnw|aaMs2wYQyBa`F0R1=ApN}h|= zK%@t)%uU!ykS9^#An&Qfs3tC!{U$v}@o)@1s_52H6;1l6Ns7{E5gSKvp(0Wl2f;Y9 zNYeBLV>~2M@AzDoSRbX-f;J&)%tM{TYl2-Q5>4n*E%TGCM)45jK_pFG6Qqh1JV~;e zm3|qipwI|5pNFt^StLOb%*F|`cEq|UC2=Yh4_K`tQRrzgPUxCVrrUy-wrK)xs%w&T zDqLmChgmm~Af`qvXp$w7jHOPM-Q-E27dK0qKwIn=NpM<|2wv92k*p9Vi4#>eHekv$ z2}uRWY0}VMtkeQaM#IC10!htL2}!S3*s?U4cb0Y8Op-@0r;V<2MUtkJJUC9I>U*w9 zt`Y`UnVKdART|MZghVA3xGYS@f+}7mPD(J;J5uHUx+RwaOI;HN#g^HI#0i-+>pXe! zm2q@fB54+Lhe`E*TllIq-pNtST8Mfl*QBZpH~CtaMH0qAl{l%a36{F`G6`W(>or*$ zFfeJXBcUugR=and(iH7Ohl@tX8M)np4vX`PKUc??_Pr2j7@ z)K9r461qd*zfcpROE0V@*URJafFtAmc1@b?7;ND+n(V`cL`|>kDbO-euF2YEY`MtT z1JPC!f0;B~TxH~e%G{T_4@2@JCxsc^YXT?m@C9d&hmz*-J1b7lX|kCHH6!S&Rug&+ zdAJBqtK)*I$FW={X@06nA2iwdufA|+Q2kd79M5bh7fCkZAWh5>h@NP|ejkWcr|D5B z*GYaN4$`E0#lNKid90%%NioT`mO4?^Bz_?Txv2Ni$RU1Ws_C2c3#A27WPv3AY6wST zaQv0dM_F$ZTZAcUg0(?cf5SDlAO7lZ(aFA9zf`IR$^r?0-7$Qy;BYme+$j6Olxm|` zCCh4({LKI94X%QIgtFYz%CiM!vnDux+W5r@CY04sEX&I@&32MDD?H(veaLjv&CIcx zT%2DfVPhv;E^d34{gBo`wCR^{nhkNX=}SknI-*xYVZ!(;P10|>a6Jo!GMo$VYbd{| z2}yK6VOVjLJ-3g<>2sRji4*#z58M~_kWg0KboD2iWM5(w65V{d(R@&v@zZZ=Qa13_ zAqi#TF1_DD{2#D<%5g32o815a002ovPDHLkV1iL~ BGNu3k literal 0 HcmV?d00001 diff --git a/src/fireedge/src/client/assets/images/logos/windowsxp.png b/src/fireedge/src/client/assets/images/logos/windowsxp.png new file mode 100644 index 0000000000000000000000000000000000000000..8503f76a00bebf76ac16df93be29525b8469a9c5 GIT binary patch literal 7276 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 'NO') } export const DEV_PREFIX = { diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js index 6031663888..43b756ac61 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/index.js @@ -14,33 +14,34 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useCallback } from 'react' +import PropTypes from 'prop-types' import FormWithSchema from 'client/components/Forms/FormWithSchema' - -import { - SCHEMA, - FIELDS -} from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/schema' +import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions/schema' import { T } from 'client/constants' export const STEP_ID = 'advanced' +const Content = ({ hypervisor }) => ( + +) + const AdvancedOptions = ({ hypervisor } = {}) => ({ id: STEP_ID, label: T.AdvancedOptions, resolver: () => SCHEMA(hypervisor), optionsValidate: { abortEarly: false }, - content: useCallback( - () => ( - - ), - [hypervisor] - ) + content: () => Content({ hypervisor }) }) +Content.propTypes = { + hypervisor: PropTypes.any, + data: PropTypes.any, + setFormData: PropTypes.func +} + export default AdvancedOptions diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js index a78626e2bb..c6d8976352 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/index.js @@ -14,55 +14,50 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useCallback } from 'react' +import PropTypes from 'prop-types' import { useListForm } from 'client/hooks' import { ImagesTable } from 'client/components/Tables' - -import { - SCHEMA -} from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema' +import { SCHEMA } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema' import { T } from 'client/constants' export const STEP_ID = 'image' +const Content = ({ data, setFormData }) => { + const { ID } = data?.[0] ?? {} + + const { + handleSelect, + handleClear + } = useListForm({ key: STEP_ID, setList: setFormData }) + + const handleSelectedRows = rows => { + const { original = {} } = rows?.[0] ?? {} + + original.ID !== undefined ? handleSelect(original) : handleClear() + } + + return ( + + ) +} + const ImageStep = () => ({ id: STEP_ID, label: T.Image, - resolver: () => SCHEMA, - content: useCallback( - ({ data, setFormData }) => { - const selectedImage = data?.[0] - - const { - handleSelect, - handleClear - } = useListForm({ key: STEP_ID, setList: setFormData }) - - const handleSelectedRows = rows => { - const { original } = rows?.[0] ?? {} - const { ID, NAME, UID, UNAME } = original ?? {} - - const image = { - IMAGE_ID: ID, - IMAGE: NAME, - IMAGE_UID: UID, - IMAGE_UNAME: UNAME - } - - ID !== undefined ? handleSelect(image) : handleClear() - } - - return ( - - ) - }, []) + resolver: SCHEMA, + content: Content }) +Content.propTypes = { + data: PropTypes.any, + setFormData: PropTypes.func +} + export default ImageStep diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js index 866b07ca12..f1024a0e2e 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable/schema.js @@ -20,4 +20,5 @@ export const SCHEMA = yup .min(1, 'Select image') .max(1, 'Max. one image selected') .required('Image field is required') - .default([]) + .ensure() + .default(() => []) diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js index ad9a6ae2cd..10767ffafb 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/ImageSteps/index.js @@ -13,10 +13,51 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import ImagesTable from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable' -import AdvancedOptions from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions' -import { createSteps } from 'client/utils' +import ImagesTable, { STEP_ID as STEP_IMAGE } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/ImagesTable' +import AdvancedOptions, { STEP_ID as STEP_ADVANCED } from 'client/components/Forms/Vm/AttachDiskForm/ImageSteps/AdvancedOptions' +import { mapUserInputs, createSteps } from 'client/utils' -const Steps = createSteps([ImagesTable, AdvancedOptions]) +const Steps = createSteps( + [ImagesTable, AdvancedOptions], + { + transformInitialValue: initialValue => { + const { + IMAGE, + IMAGE_ID, + IMAGE_UID, + IMAGE_UNAME, + IMAGE_STATE, + ...diskProps + } = initialValue ?? {} + + return { + [STEP_IMAGE]: [{ + ...diskProps, + NAME: IMAGE, + ID: IMAGE_ID, + UID: IMAGE_UID, + UNAME: IMAGE_UNAME, + STATE: IMAGE_STATE + }], + [STEP_ADVANCED]: initialValue + } + }, + transformBeforeSubmit: formData => { + const { [STEP_IMAGE]: [image] = [], [STEP_ADVANCED]: advanced } = formData + const { ID, NAME, UID, UNAME, STATE, SIZE, ...imageProps } = image ?? {} + + return { + ...imageProps, + IMAGE: NAME, + IMAGE_ID: ID, + IMAGE_UID: UID, + IMAGE_UNAME: UNAME, + IMAGE_STATE: STATE, + ORIGINAL_SIZE: SIZE, + ...mapUserInputs(advanced) + } + } + } +) export default Steps diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js index bbf54c4813..db344cd5ba 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/index.js @@ -14,31 +14,34 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useCallback } from 'react' +import PropTypes from 'prop-types' import FormWithSchema from 'client/components/Forms/FormWithSchema' - -import { - SCHEMA, - FIELDS -} from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/schema' +import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions/schema' import { T } from 'client/constants' export const STEP_ID = 'advanced' +const Content = ({ hypervisor }) => ( + +) + const AdvancedOptions = ({ hypervisor } = {}) => ({ id: STEP_ID, label: T.AdvancedOptions, resolver: () => SCHEMA(hypervisor), optionsValidate: { abortEarly: false }, - content: useCallback( - () => , - [hypervisor] - ) + content: () => Content({ hypervisor }) }) +Content.propTypes = { + hypervisor: PropTypes.any, + data: PropTypes.any, + setFormData: PropTypes.func +} + export default AdvancedOptions diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js index c62bc31cc5..5069290aa4 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/index.js @@ -14,31 +14,34 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useCallback } from 'react' +import PropTypes from 'prop-types' import FormWithSchema from 'client/components/Forms/FormWithSchema' - -import { - SCHEMA, - FIELDS -} from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/schema' +import { SCHEMA, FIELDS } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration/schema' import { T } from 'client/constants' export const STEP_ID = 'configuration' +const Content = ({ hypervisor }) => ( + +) + const BasicConfiguration = ({ hypervisor } = {}) => ({ id: STEP_ID, label: T.Configuration, resolver: () => SCHEMA(hypervisor), optionsValidate: { abortEarly: false }, - content: useCallback( - () => , - [hypervisor] - ) + content: () => Content({ hypervisor }) }) +Content.propTypes = { + hypervisor: PropTypes.any, + data: PropTypes.any, + setFormData: PropTypes.func +} + export default BasicConfiguration diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js index c8df7059ba..29fdbb810e 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachDiskForm/VolatileSteps/index.js @@ -13,10 +13,28 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import BasicConfiguration from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration' -import AdvancedOptions from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions' -import { createSteps } from 'client/utils' +import BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/BasicConfiguration' +import AdvancedOptions, { STEP_ID as ADVANCED_ID } from 'client/components/Forms/Vm/AttachDiskForm/VolatileSteps/AdvancedOptions' +import { mapUserInputs, createSteps } from 'client/utils' -const Steps = createSteps([BasicConfiguration, AdvancedOptions]) +const Steps = createSteps( + [BasicConfiguration, AdvancedOptions], + { + transformInitialValue: (disk = {}, schema) => ({ + ...schema.cast({ + [BASIC_ID]: disk, + [ADVANCED_ID]: disk + }, { stripUnknown: true }) + }), + transformBeforeSubmit: formData => { + const { + [BASIC_ID]: configuration = {}, + [ADVANCED_ID]: advanced = {} + } = formData ?? {} + + return { ...mapUserInputs(advanced), ...mapUserInputs(configuration) } + } + } +) export default Steps diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js index 0a8c73a6c4..d1e3006421 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable/schema.js @@ -20,4 +20,5 @@ export const SCHEMA = yup .min(1, 'Select network') .max(1, 'Max. one network selected') .required('Network field is required') - .default([]) + .ensure() + .default(() => []) diff --git a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js index 3a3e6f915c..4622c823bf 100644 --- a/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/Vm/AttachNicForm/Steps/index.js @@ -13,16 +13,38 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import NetworksTable, { STEP_ID as NETWORK_STEP } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable' -import AdvancedOptions, { STEP_ID as ADVANCED_STEP } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions' +import NetworksTable, { STEP_ID as NETWORK_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable' +import AdvancedOptions, { STEP_ID as ADVANCED_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions' import { mapUserInputs, createSteps } from 'client/utils' const Steps = createSteps( [NetworksTable, AdvancedOptions], { + transformInitialValue: nic => { + const { + NETWORK, + NETWORK_ID: ID, + NETWORK_UID, + NETWORK_UNAME, + SECURITY_GROUPS, + ...rest + } = nic ?? {} + + return { + [NETWORK_ID]: [{ + ...nic, + ID, + NAME: NETWORK, + UID: NETWORK_UID, + UNAME: NETWORK_UNAME, + SECURITY_GROUPS + }], + [ADVANCED_ID]: rest + } + }, transformBeforeSubmit: formData => { - const { [NETWORK_STEP]: network, [ADVANCED_STEP]: advanced } = formData - const { ID, NAME, UID, UNAME, SECURITY_GROUPS } = network?.[0] + const { [NETWORK_ID]: [network] = [], [ADVANCED_ID]: advanced } = formData + const { ID, NAME, UID, UNAME, SECURITY_GROUPS } = network ?? {} return { NETWORK_ID: ID, @@ -32,19 +54,6 @@ const Steps = createSteps( SECURITY_GROUPS, ...mapUserInputs(advanced) } - }, - transformInitialValue: initialValue => { - const { NETWORK_ID, NETWORK, NETWORK_UID, NETWORK_UNAME, ...rest } = initialValue ?? {} - - return { - [NETWORK_STEP]: [{ - ID: NETWORK_ID, - NAME: NETWORK, - UID: NETWORK_UID, - UNAME: NETWORK_UNAME - }], - [ADVANCED_STEP]: rest - } } } ) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js new file mode 100644 index 0000000000..87ed298d18 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/index.js @@ -0,0 +1,21 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { createForm } from 'client/utils' +import { SCHEMA, FIELDS } from 'client/components/Forms/VmTemplate/CloneForm/schema' + +const CloneForm = createForm(SCHEMA, FIELDS) + +export default CloneForm diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js new file mode 100644 index 0000000000..3ecf1b3409 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CloneForm/schema.js @@ -0,0 +1,59 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +/* eslint-disable jsdoc/require-jsdoc */ +import { string, boolean, object } from 'yup' +import { INPUT_TYPES } from 'client/constants' +import { getValidationFromFields } from 'client/utils' + +const PREFIX = { + name: 'prefix', + label: 'Prefix', + tooltip: ` + Several templates are selected, + please choose prefix to name the new copies.`, + type: INPUT_TYPES.TEXT, + validation: string() + .trim() + .required('Prefix field is required') + .default(() => 'Copy of ') +} + +const NAME = { + name: 'name', + label: 'Name', + tooltip: 'Name for the new template.', + type: INPUT_TYPES.TEXT, + validation: string() + .trim() + .required('Name field is required') + .default(() => '') +} + +const IMAGES = { + name: 'image', + label: 'Clone with images', + tooltip: ` + You can also clone any Image referenced inside this Template. + They will be cloned to a new Image, and made persistent.`, + type: INPUT_TYPES.CHECKBOX, + validation: boolean().default(() => false), + grid: { md: 12 } +} + +export const FIELDS = ({ isMultiple } = {}) => + [isMultiple ? PREFIX : NAME, IMAGES] + +export const SCHEMA = props => object(getValidationFromFields(FIELDS(props))) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js index 6b10bd9a54..bb18176c70 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js @@ -14,13 +14,9 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useMemo } from 'react' -import { useFormContext } from 'react-hook-form' - import FormWithSchema from 'client/components/Forms/FormWithSchema' import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/styles' -import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable' import { SCHEMA, FIELDS } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema' import { Tr } from 'client/components/HOC' import { T } from 'client/constants' @@ -29,8 +25,6 @@ export const STEP_ID = 'configuration' const Content = () => { const classes = useStyles() - const { watch } = useFormContext() - const selectedVmTemplate = useMemo(() => watch(`${TEMPLATE_ID}[0]`), []) return (
@@ -47,12 +41,6 @@ const Content = () => { legend={Tr(T.Capacity)} id={STEP_ID} /> - DISK_FIELDS(vmTemplate), + // DISK: vmTemplate => DISK_FIELDS(vmTemplate), OWNERSHIP: OWNERSHIP_FIELDS, VM_GROUP: VM_GROUP_FIELDS, VCENTER: VCENTER_FIELDS @@ -35,7 +34,7 @@ export const FIELDS = { export const SCHEMA = object() .concat(INFORMATION_SCHEMA) .concat(CAPACITY_SCHEMA) - .concat(DISK_SCHEMA) + // .concat(DISK_SCHEMA) .concat(OWNERSHIP_SCHEMA) .concat(VM_GROUP_SCHEMA) .concat(VCENTER_SCHEMA) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js index 2cb05baf30..45f10bac8b 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting.js @@ -14,7 +14,7 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ -import { useMemo, SetStateAction } from 'react' +import { SetStateAction } from 'react' import PropTypes from 'prop-types' import { @@ -25,11 +25,10 @@ import { } from 'iconoir-react' import { Divider, makeStyles } from '@material-ui/core' import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd' -import { useFormContext } from 'react-hook-form' import { Translate } from 'client/components/HOC' import { Action } from 'client/components/Cards/SelectCard' -import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable' +import { TAB_ID as STORAGE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage' import { TAB_ID as NIC_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking' import { set } from 'client/utils' import { T } from 'client/constants' @@ -54,10 +53,40 @@ const useStyles = makeStyles(theme => ({ })) /** - * @param {string[]} newBootOrder - New boot order - * @param {SetStateAction} setFormData - New boot order + * @param {string} id - Resource id: 'NIC' or 'DISK' + * @param {Array} list - List of resources + * @param {object} formData - Form data + * @param {SetStateAction} setFormData - React set state action */ -export const reorder = (newBootOrder, setFormData) => { +export const reorderBootAfterRemove = (id, list, formData, setFormData) => { + const type = String(id).toLowerCase().replace(/\d+/g, '') // nic | disk + const getIndexFromId = id => String(id).toLowerCase().replace(type, '') + const idxToRemove = getIndexFromId(id) + + const ids = list + .filter(resource => resource.NAME !== id) + .map(resource => String(resource.NAME).toLowerCase()) + + const newBootOrder = [...formData?.OS?.BOOT?.split(',').filter(Boolean)] + .filter(bootId => !bootId.startsWith(type) || ids.includes(bootId)) + .map(bootId => { + if (!bootId.startsWith(type)) return bootId + + const resourceId = getIndexFromId(bootId) + + return resourceId < idxToRemove + ? bootId + : `${type}${resourceId - 1}` + }) + + reorder(newBootOrder, setFormData) +} + +/** + * @param {string[]} newBootOrder - New boot order + * @param {SetStateAction} setFormData - React set state action + */ +const reorder = (newBootOrder, setFormData) => { setFormData(prev => { const newData = set({ ...prev }, 'extra.OS.BOOT', newBootOrder.join(',')) @@ -67,41 +96,32 @@ export const reorder = (newBootOrder, setFormData) => { const Booting = ({ data, setFormData }) => { const classes = useStyles() - const { watch } = useFormContext() const bootOrder = data?.OS?.BOOT?.split(',').filter(Boolean) ?? [] - const disks = useMemo(() => { - const templateSeleted = watch(`${TEMPLATE_ID}[0]`) - const listOfDisks = [templateSeleted?.TEMPLATE?.DISK ?? []].flat() - - return listOfDisks?.map(disk => { - const { DISK_ID, IMAGE, IMAGE_ID } = disk - const isVolatile = !IMAGE && !IMAGE_ID - - const name = isVolatile - ? <>`DISK ${DISK_ID}: ` - : `DISK ${DISK_ID}: ${IMAGE}` + const disks = data?.[STORAGE_ID] + ?.map((disk, idx) => { + const isVolatile = !disk?.IMAGE && !disk?.IMAGE_ID return { - ID: `disk${DISK_ID}`, + ID: `disk${idx}`, NAME: ( <> - {name} + {isVolatile + ? <>{`${disk?.NAME}: `} + : [disk?.NAME, disk?.IMAGE].filter(Boolean).join(': ')} ) } - }) - }, []) + }) ?? [] const nics = data?.[NIC_ID] - ?.map((nic, idx) => ({ ...nic, NAME: nic?.NAME ?? `NIC${idx}` })) ?.map((nic, idx) => ({ ID: `nic${idx}`, NAME: ( <> - {[nic?.NAME ?? `NIC${idx}`, nic.NETWORK].filter(Boolean).join(': ')} + {[nic?.NAME, nic.NETWORK].filter(Boolean).join(': ')} ) })) ?? [] diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js index dcd13b88cd..2e5cfaeb62 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/index.js @@ -22,6 +22,7 @@ import { WarningCircledOutline as WarningIcon } from 'iconoir-react' import Tabs from 'client/components/Tabs' import { SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema' +import Storage from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage' import Networking from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking' import Placement from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/placement' import ScheduleAction from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/scheduleAction' @@ -35,6 +36,10 @@ const Content = ({ data, setFormData }) => { const { errors } = useFormContext() const tabs = [ + { + name: 'storage', + renderContent: Storage({ data, setFormData }) + }, { name: 'network', renderContent: Networking({ data, setFormData }) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js index fcdf2677ec..75ce48d1a5 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/networking.js @@ -25,8 +25,9 @@ import { AttachNicForm } from 'client/components/Forms/Vm' import { Tr, Translate } from 'client/components/HOC' import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration' -import { NIC_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema' -import { reorder } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting' +import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema' +import { reorderBootAfterRemove } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting' +import { stringToBoolean } from 'client/models/Helper' import { T } from 'client/constants' const useStyles = makeStyles({ @@ -43,36 +44,21 @@ export const TAB_ID = 'NIC' const Networking = ({ data, setFormData }) => { const classes = useStyles() const nics = data?.[TAB_ID] - ?.map((nic, idx) => ({ ...nic, NAME: nic?.NAME ?? `NIC${idx}` })) - const { handleRemove, handleSave } = useListForm({ + const { handleSetList, handleRemove, handleSave } = useListForm({ parent: EXTRA_ID, key: TAB_ID, list: nics, setList: setFormData, - getItemId: (item) => item.NAME ?? `NIC${data.length + 1}`, - addItemId: (item, id) => ({ ...item, NAME: id }) + getItemId: item => item.NAME, + addItemId: (item, _, itemIndex) => ({ ...item, NAME: `${TAB_ID}${itemIndex}` }) }) - const reorderBootOrder = nicId => { - const getIndexFromNicId = id => String(id).toLowerCase().replace('nic', '') - const idxToRemove = getIndexFromNicId(nicId) + const reorderNics = () => { + const diskSchema = EXTRA_SCHEMA.pick([TAB_ID]) + const { [TAB_ID]: newList } = diskSchema.cast({ [TAB_ID]: data?.[TAB_ID] }) - const nicIds = nics - .filter(nic => nic.NAME !== nicId) - .map(nic => String(nic.NAME).toLowerCase()) - - const newBootOrder = [...data?.OS?.BOOT?.split(',').filter(Boolean)] - .filter(bootId => !bootId.startsWith('nic') || nicIds.includes(bootId)) - .map(bootId => { - if (!bootId.startsWith('nic')) return bootId - - const nicId = getIndexFromNicId(bootId) - - return nicId < idxToRemove ? bootId : `nic${nicId - 1}` - }) - - reorder(newBootOrder, setFormData) + handleSetList(newList) } return ( @@ -88,8 +74,7 @@ const Networking = ({ data, setFormData }) => { }} options={[{ form: () => AttachNicForm({ nics }), - onSubmit: formData => - handleSave(NIC_SCHEMA.cast(formData)) + onSubmit: handleSave }]} />
@@ -103,7 +88,12 @@ const Networking = ({ data, setFormData }) => { title={[NAME, NETWORK].filter(Boolean).join(' - ')} subheader={<> {Object - .entries({ RDP, SSH, ALIAS: PARENT, EXTERNAL }) + .entries({ + RDP: stringToBoolean(RDP), + SSH: stringToBoolean(SSH), + EXTERNAL: stringToBoolean(EXTERNAL), + ALIAS: PARENT + }) .map(([k, v]) => v ? `${k}` : '') .filter(Boolean) .join(' | ') @@ -116,7 +106,8 @@ const Networking = ({ data, setFormData }) => { data-cy={`remove-${NAME}`} handleClick={() => { handleRemove(NAME) - reorderBootOrder(NAME) + reorderNics() + reorderBootAfterRemove(NAME, nics, data, setFormData) }} icon={} /> diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js index 4840939958..4e8576a2f3 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema.js @@ -17,7 +17,6 @@ import { array, object, string, lazy } from 'yup' import { v4 as uuidv4 } from 'uuid' -import { SCHEMA as NETWORK_SCHEMA } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions/schema' import { SCHEMA as PUNCTUAL_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/PunctualForm/schema' import { SCHEMA as RELATIVE_SCHEMA } from 'client/components/Forms/Vm/CreateSchedActionForm/RelativeForm/schema' import { INPUT_TYPES } from 'client/constants' @@ -65,14 +64,6 @@ export const DS_RANK_FIELD = { validation: string().trim().notRequired() } -export const NIC_SCHEMA = object({ - NAME: string().trim(), - NETWORK_ID: string().trim(), - NETWORK: string().trim(), - NETWORK_UNAME: string().trim(), - SECURITY_GROUPS: string().trim() -}).concat(NETWORK_SCHEMA) - export const SCHED_ACTION_SCHEMA = lazy(({ TIME } = {}) => { const isRelative = String(TIME).includes('+') const schema = isRelative ? RELATIVE_SCHEMA : PUNCTUAL_SCHEMA @@ -81,7 +72,22 @@ export const SCHED_ACTION_SCHEMA = lazy(({ TIME } = {}) => { }) export const SCHEMA = object({ - NIC: array(NIC_SCHEMA).ensure(), + DISK: array() + .ensure() + .transform(disks => disks?.map((disk, idx) => ({ + ...disk, + NAME: disk?.NAME?.startsWith('DISK') || !disk?.NAME + ? `DISK${idx}` + : disk?.NAME + }))), + NIC: array() + .ensure() + .transform(nics => nics?.map((nic, idx) => ({ + ...nic, + NAME: nic?.NAME?.startsWith('NIC') || !nic?.NAME + ? `NIC${idx}` + : nic?.NAME + }))), SCHED_ACTION: array(SCHED_ACTION_SCHEMA).ensure(), OS: object({ BOOT: string().trim().notRequired() @@ -92,4 +98,4 @@ export const SCHEMA = object({ DS_REQ_FIELD, DS_RANK_FIELD ]) -}) +}).noUnknown(false) diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js new file mode 100644 index 0000000000..2303707f1a --- /dev/null +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/storage.js @@ -0,0 +1,194 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +/* eslint-disable jsdoc/require-jsdoc */ +import { useMemo } from 'react' +import PropTypes from 'prop-types' +import { useFormContext } from 'react-hook-form' +import { makeStyles } from '@material-ui/core' +import { Edit, Trash } from 'iconoir-react' + +import { useListForm } from 'client/hooks' +import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm' +import SelectCard, { Action } from 'client/components/Cards/SelectCard' +import { ImageSteps, VolatileSteps } from 'client/components/Forms/Vm' +import { StatusCircle, StatusChip } from 'client/components/Status' +import { Tr, Translate } from 'client/components/HOC' + +import { STEP_ID as TEMPLATE_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable' +import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration' +import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema' +import { reorderBootAfterRemove } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/booting' +import { getState, getDiskType } from 'client/models/Image' +import { stringToBoolean } from 'client/models/Helper' +import { prettyBytes } from 'client/utils' +import { T } from 'client/constants' + +const useStyles = makeStyles({ + root: { + paddingBlock: '1em', + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(300px, auto))', + gap: '1em' + } +}) + +export const TAB_ID = 'DISK' + +const Storage = ({ data, setFormData }) => { + const classes = useStyles() + const { watch } = useFormContext() + const { HYPERVISOR } = useMemo(() => watch(`${TEMPLATE_ID}[0]`) ?? {}, []) + const disks = data?.[TAB_ID] + + const { handleSetList, handleRemove, handleSave } = useListForm({ + parent: EXTRA_ID, + key: TAB_ID, + list: disks, + setList: setFormData, + getItemId: item => item.NAME, + addItemId: (item, _, itemIndex) => ({ ...item, NAME: `${TAB_ID}${itemIndex}` }) + }) + + const reorderDisks = () => { + const diskSchema = EXTRA_SCHEMA.pick([TAB_ID]) + const { [TAB_ID]: newList } = diskSchema.cast({ [TAB_ID]: data?.[TAB_ID] }) + + handleSetList(newList) + } + + return ( + <> + ImageSteps({ hypervisor: HYPERVISOR }), + onSubmit: handleSave + }, + { + cy: 'attach-volatile-disk', + name: T.Volatile, + form: () => VolatileSteps({ hypervisor: HYPERVISOR }), + onSubmit: handleSave + } + ]} + /> +
+ {disks?.map(item => { + const { + NAME, + TYPE, + IMAGE, + IMAGE_ID, + IMAGE_STATE, + ORIGINAL_SIZE, + SIZE = ORIGINAL_SIZE, + READONLY, + DATASTORE, + PERSISTENT + } = item + + const isVolatile = !IMAGE && !IMAGE_ID + const isPersistent = stringToBoolean(PERSISTENT) + const state = !isVolatile && getState({ STATE: IMAGE_STATE }) + const type = isVolatile ? TYPE : getDiskType(item) + const originalSize = +ORIGINAL_SIZE ? prettyBytes(+ORIGINAL_SIZE, 'MB') : '-' + const size = prettyBytes(+SIZE, 'MB') + + return ( + + {`${NAME} - `} + + + ) : ( + + + {`${NAME}: ${IMAGE}`} + {isPersistent && } + + )} + subheader={<> + {Object + .entries({ + [DATASTORE]: DATASTORE, + READONLY: stringToBoolean(READONLY), + PERSISTENT: stringToBoolean(PERSISTENT), + [isVolatile || ORIGINAL_SIZE === SIZE ? size : `${originalSize}/${size}`]: true, + [type]: type + }) + .map(([k, v]) => v ? `${k}` : '') + .filter(Boolean) + .join(' | ') + } + } + action={ + <> + } + handleClick={() => { + handleRemove(NAME) + reorderDisks() + reorderBootAfterRemove(NAME, disks, data, setFormData) + }} + icon={} + /> + , + tooltip: + }} + dialogProps={{ + title: <>{`: ${NAME}`} + }} + options={[{ + form: () => isVolatile + ? VolatileSteps({ hypervisor: HYPERVISOR }, item) + : ImageSteps({ hypervisor: HYPERVISOR }, item), + onSubmit: newValues => handleSave(newValues, NAME) + }]} + /> + + } + /> + ) + })} +
+ + ) +} + +Storage.propTypes = { + data: PropTypes.any, + setFormData: PropTypes.func +} + +Storage.displayName = 'Storage' + +export default Storage diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js index 5f7501ffca..8fa507edfa 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/VmTemplatesTable/index.js @@ -15,6 +15,7 @@ * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ import PropTypes from 'prop-types' +import { makeStyles } from '@material-ui/core' import { useListForm } from 'client/hooks' import { useVmTemplateApi } from 'client/features/One' @@ -29,7 +30,14 @@ import { T } from 'client/constants' export const STEP_ID = 'template' +const useStyles = makeStyles({ + body: { + gridTemplateColumns: 'repeat(auto-fill, minmax(400px, 1fr))' + } +}) + const Content = ({ data, setFormData }) => { + const classes = useStyles() const selectedTemplate = data?.[0] const { getVmTemplate } = useVmTemplateApi() @@ -62,6 +70,7 @@ const Content = ({ data, setFormData }) => { singleSelect onlyGlobalSearch onlyGlobalSelectedRows + classes={classes} initialState={{ selectedRowIds: { [selectedTemplate?.ID]: true } }} onSelectedRowsChange={handleSelectedRows} /> diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js index e5ca99d541..dfe51a8633 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js @@ -17,7 +17,7 @@ import VmTemplatesTable, { STEP_ID as TEMPLATE_ID } from 'client/components/Form import BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration' import ExtraConfiguration, { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration' import { jsonToXml } from 'client/models/Helper' -import { createSteps, deepmerge } from 'client/utils' +import { createSteps } from 'client/utils' const Steps = createSteps( [VmTemplatesTable, BasicConfiguration, ExtraConfiguration], @@ -27,7 +27,7 @@ const Steps = createSteps( [TEMPLATE_ID]: [vmTemplate], [BASIC_ID]: vmTemplate?.TEMPLATE, [EXTRA_ID]: vmTemplate?.TEMPLATE - }, { stripUnknown: true }) + }, { stripUnknown: true, context: { [TEMPLATE_ID]: [vmTemplate] } }) }), transformBeforeSubmit: formData => { const { @@ -37,8 +37,7 @@ const Steps = createSteps( } = formData ?? {} // merge with template disks to get TYPE attribute - const DISK = deepmerge(templateSelected.TEMPLATE?.DISK, restOfConfig?.DISK) - const templateXML = jsonToXml({ ...extraTemplate, ...restOfConfig, DISK }) + const templateXML = jsonToXml({ ...extraTemplate, ...restOfConfig }) const data = { instances, hold, persistent, template: templateXML } const templates = [...new Array(instances)] diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/index.js index 43828f9b54..2b2c302b9c 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/index.js @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ +import CloneForm from 'client/components/Forms/VmTemplate/CloneForm' import InstantiateForm from 'client/components/Forms/VmTemplate/InstantiateForm' export { + CloneForm, InstantiateForm } diff --git a/src/fireedge/src/client/components/Icons/opennebula.js b/src/fireedge/src/client/components/Icons/opennebula.js index 4af4161754..9a5d8e57d8 100644 --- a/src/fireedge/src/client/components/Icons/opennebula.js +++ b/src/fireedge/src/client/components/Icons/opennebula.js @@ -60,26 +60,28 @@ const OpenNebulaLogo = memo(({ width, height, spinner, withText, viewBox, ...pro )} {/* --------------- CLOUD ------------------ */} - - - - - + + + + + + + {withText && ( diff --git a/src/fireedge/src/client/components/Image/index.js b/src/fireedge/src/client/components/Image/index.js index abfab0a17c..b43b061606 100644 --- a/src/fireedge/src/client/components/Image/index.js +++ b/src/fireedge/src/client/components/Image/index.js @@ -47,7 +47,12 @@ const Image = memo(({ src, imageInError, withSources, imgProps }) => { } if (error.fail) { - return + return } return ( diff --git a/src/fireedge/src/client/components/Tables/Enhanced/index.js b/src/fireedge/src/client/components/Tables/Enhanced/index.js index f864cc28fe..2b943a6190 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/index.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/index.js @@ -17,6 +17,7 @@ import { useMemo } from 'react' import PropTypes from 'prop-types' +import clsx from 'clsx' import { InfoEmpty } from 'iconoir-react' import { Box, LinearProgress } from '@material-ui/core' import { @@ -54,9 +55,10 @@ const EnhancedTable = ({ pageSize = 10, RowComponent, showPageCount, - singleSelect = false + singleSelect = false, + classes = {} }) => { - const classes = EnhancedTableStyles() + const styles = EnhancedTableStyles() const isFetching = isLoading && data === undefined @@ -127,8 +129,8 @@ const EnhancedTable = ({ } return ( - -
+ +
{/* TOOLBAR */} {!isFetching && ( +
{page?.length > 0 && ( {isLoading && ( - + )} -
+
{/* FILTERS */} {!isFetching && ( )} -
+
{/* NO DATA MESSAGE */} {!isFetching && page?.length === 0 && ( - + @@ -209,6 +211,10 @@ export const EnhancedTableProps = { fetchMore: PropTypes.func, getRowId: PropTypes.func, initialState: PropTypes.object, + classes: PropTypes.shape({ + root: PropTypes.string, + body: PropTypes.string + }), isLoading: PropTypes.bool, onlyGlobalSearch: PropTypes.bool, onlyGlobalSelectedRows: PropTypes.bool, diff --git a/src/fireedge/src/client/components/Tables/Enhanced/styles.js b/src/fireedge/src/client/components/Tables/Enhanced/styles.js index 7439bbecfd..bc6e057123 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/styles.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/styles.js @@ -58,6 +58,7 @@ export default makeStyles( display: 'grid', gap: '1em', gridTemplateColumns: 'minmax(0, 1fr)', + // gridTemplateRows: 'repeat(auto-fill, 10em)', gridAutoRows: 'max-content', paddingBlock: '0.8em', '& > [role=row]': { @@ -68,9 +69,9 @@ export default makeStyles( fontWeight: typography.fontWeightMedium, fontSize: '1em', border: `1px solid ${palette.divider}`, - borderRadius: 6, + borderRadius: '0.5em', display: 'flex', - gap: 8, + gap: '1em', '&:hover': { backgroundColor: palette.action.hover }, diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/actions.js b/src/fireedge/src/client/components/Tables/VmTemplates/actions.js index d5b9b73cde..c7707f8469 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/actions.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/actions.js @@ -33,6 +33,7 @@ import { import { useAuth } from 'client/features/Auth' import { useVmTemplateApi } from 'client/features/One' +import { CloneForm } from 'client/components/Forms/VmTemplate' import { createActions } from 'client/components/Tables/Enhanced/Utils' import { PATH } from 'client/apps/sunstone/routesOne' import { VM_TEMPLATE_ACTIONS, MARKETPLACE_APP_ACTIONS } from 'client/constants' @@ -40,7 +41,14 @@ import { VM_TEMPLATE_ACTIONS, MARKETPLACE_APP_ACTIONS } from 'client/constants' const Actions = () => { const history = useHistory() const { view, getResourceView } = useAuth() - const { getVmTemplate, getVmTemplates, lock, unlock, remove } = useVmTemplateApi() + const { + getVmTemplate, + getVmTemplates, + lock, + unlock, + clone, + remove + } = useVmTemplateApi() const vmTemplateActions = useMemo(() => createActions({ filters: getResourceView('VM-TEMPLATE')?.actions, @@ -106,8 +114,42 @@ const Actions = () => { label: 'Clone', tooltip: 'Clone', selected: true, - disabled: true, - options: [{ onSubmit: () => undefined }] + dialogProps: { + title: rows => { + const isMultiple = rows?.length > 1 + const { ID, NAME } = rows?.[0]?.original + + return [ + isMultiple ? 'Clone several Templates' : 'Clone Template', + !isMultiple && `#${ID} ${NAME}` + ].filter(Boolean).join(' - ') + } + }, + options: [{ + form: rows => { + const vmTemplates = rows?.map(({ original }) => original) + const stepProps = { isMultiple: vmTemplates.length > 1 } + const initialValues = { name: `Copy of ${vmTemplates?.[0]?.NAME}` } + + return CloneForm(stepProps, initialValues) + }, + onSubmit: async (formData, rows) => { + try { + const { prefix } = formData + + const vmTemplates = rows?.map?.(({ original: { ID, NAME } = {} }) => { + // overwrite all names with prefix+NAME + const formatData = prefix ? { name: `${prefix} ${NAME}` } : {} + + return { id: ID, data: { ...formData, ...formatData } } + }) + + await Promise.all(vmTemplates.map(({ id, data }) => clone(id, data))) + } finally { + await getVmTemplates() + } + } + }] }, { tooltip: 'Change ownership', @@ -157,7 +199,7 @@ const Actions = () => { isConfirmDialog: true, onSubmit: async (_, rows) => { const templateIds = rows?.map?.(({ original }) => original?.ID) - await Promise.all([...new Array(templateIds)].map(id => lock(id))) + await Promise.all(templateIds.map(id => lock(id))) await Promise.all(templateIds.map(id => getVmTemplate(id))) } }] @@ -178,7 +220,7 @@ const Actions = () => { options: [{ isConfirmDialog: true, onSubmit: async (_, rows) => { - const templateIds = [...new Array(rows?.map?.(({ original }) => original?.ID))] + const templateIds = rows?.map?.(({ original }) => original?.ID) await Promise.all(templateIds.map(id => unlock(id))) await Promise.all(templateIds.map(id => getVmTemplate(id))) } @@ -199,7 +241,7 @@ const Actions = () => { options: [{ isConfirmDialog: true, onSubmit: async (_, rows) => { - const templateIds = [...new Array(rows?.map?.(({ original }) => original?.ID))] + const templateIds = rows?.map?.(({ original }) => original?.ID) await Promise.all(templateIds.map(id => remove(id))) await getVmTemplates() } diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js index ce21cd4a1c..f5ca3471eb 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/columns.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/columns.js @@ -16,6 +16,7 @@ /* eslint-disable jsdoc/require-jsdoc */ import { CategoryFilter } from 'client/components/Tables/Enhanced/Utils' import * as Helper from 'client/models/Helper' +import { } from 'client/constants' export default [ { Header: 'ID', accessor: 'ID', sortType: 'number' }, @@ -24,6 +25,11 @@ export default [ { Header: 'Group', accessor: 'GNAME' }, { Header: 'Start Time', accessor: 'REGTIME' }, { Header: 'Locked', accessor: 'LOCK' }, + { + Header: 'Logo', + id: 'LOGO', + accessor: row => row?.TEMPLATE?.LOGO + }, { Header: 'Virtual Router', id: 'VROUTER', diff --git a/src/fireedge/src/client/components/Tables/VmTemplates/row.js b/src/fireedge/src/client/components/Tables/VmTemplates/row.js index db62a56aa4..c61c075a7c 100644 --- a/src/fireedge/src/client/components/Tables/VmTemplates/row.js +++ b/src/fireedge/src/client/components/Tables/VmTemplates/row.js @@ -14,6 +14,7 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ /* eslint-disable jsdoc/require-jsdoc */ +import { useMemo } from 'react' import PropTypes from 'prop-types' import { User, Group, Lock } from 'iconoir-react' @@ -21,18 +22,38 @@ import { Typography } from '@material-ui/core' import { StatusChip } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import Image from 'client/components/Image' import * as Helper from 'client/models/Helper' +import { isExternalURL } from 'client/utils' +import { LOGO_IMAGES_URL } from 'client/constants' const Row = ({ original, value, ...props }) => { const classes = rowStyles() - const { ID, NAME, UNAME, GNAME, REGTIME, LOCK, VROUTER } = value + const { ID, NAME, UNAME, GNAME, REGTIME, LOCK, VROUTER, LOGO = '' } = value + const [logoSource] = useMemo(() => { + const external = isExternalURL(LOGO) + const cleanLogoAttribute = String(LOGO).split('/').at(-1) + const src = external ? LOGO : `${LOGO_IMAGES_URL}/${cleanLogoAttribute}` + + return [src, external] + }, [LOGO]) + + const logo = String(LOGO).split('/').at(-1) const time = Helper.timeFromMilliseconds(+REGTIME) const timeAgo = `registered ${time.toRelative()}` return (
+ {logo && ( +
+ +
+ )}
@@ -44,7 +65,7 @@ const Row = ({ original, value, ...props }) => {
- + {`#${ID} ${timeAgo}`} diff --git a/src/fireedge/src/client/components/Tables/styles.js b/src/fireedge/src/client/components/Tables/styles.js index 107fcc0c09..e14675fce9 100644 --- a/src/fireedge/src/client/components/Tables/styles.js +++ b/src/fireedge/src/client/components/Tables/styles.js @@ -31,9 +31,25 @@ export const rowStyles = makeStyles( flexWrap: 'wrap' } }, + figure: { + flexBasis: '20%', + paddingTop: '12.5%', + overflow: 'hidden', + position: 'relative' + }, + image: { + top: 0, + left: 0, + width: '100%', + height: '100%', + objectFit: 'contain', + position: 'absolute', + userSelect: 'none' + }, main: { flex: 'auto', - overflow: 'hidden' + overflow: 'hidden', + alignSelf: 'center' }, title: { color: palette.text.primary, @@ -52,8 +68,12 @@ export const rowStyles = makeStyles( color: palette.text.secondary, marginTop: 4, display: 'flex', - gap: 8, - wordWrap: 'break-word' + gap: '0.5em', + flexWrap: 'wrap', + wordWrap: 'break-word', + '& > .full-width': { + flexBasis: '100%' + } }, secondary: { width: '25%', diff --git a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js index 75a00b13ee..99e756b308 100644 --- a/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js +++ b/src/fireedge/src/client/components/Tabs/Vm/Storage/index.js @@ -38,11 +38,8 @@ const VmStorageTab = ({ tabProps: { actions } = {} }) => { const hypervisor = VirtualMachine.getHypervisor(vm) const actionsAvailable = Helper.getActionsAvailable(actions, hypervisor) - const handleAttachDisk = async ({ image, advanced, configuration }) => { - const imageSelected = image?.[0] - const root = { ...imageSelected, ...advanced, ...configuration } - - const template = Helper.jsonToXml({ DISK: root }) + const handleAttachDisk = async formData => { + const template = Helper.jsonToXml({ DISK: formData }) await attachDisk(vm.ID, template) } diff --git a/src/fireedge/src/client/constants/image.js b/src/fireedge/src/client/constants/image.js index dc0c68980e..f0231e6c45 100644 --- a/src/fireedge/src/client/constants/image.js +++ b/src/fireedge/src/client/constants/image.js @@ -16,31 +16,51 @@ import * as STATES from 'client/constants/states' import COLOR from 'client/constants/color' -/** - * @enum {( - * 'OS'| - * 'CD ROM'| - * 'DATABLOCK'| - * 'KERNEL'| - * 'RAMDISK'| - * 'CONTEXT' - * )} Image type - */ +/** @enum {string} Image type */ +export const IMAGE_TYPES_STR = { + OS: 'OS', + CDROM: 'CDROM', + DATABLOCK: 'DATABLOCK', + KERNEL: 'KERNEL', + RAMDISK: 'RAMDISK', + CONTEXT: 'CONTEXT' +} + +/** @type {IMAGE_TYPES_STR[]} Return the string representation of an Image type */ export const IMAGE_TYPES = [ - 'OS', - 'CD ROM', - 'DATABLOCK', - 'KERNEL', - 'RAMDISK', - 'CONTEXT' + IMAGE_TYPES_STR.OS, + IMAGE_TYPES_STR.CDROM, + IMAGE_TYPES_STR.DATABLOCK, + IMAGE_TYPES_STR.KERNEL, + IMAGE_TYPES_STR.RAMDISK, + IMAGE_TYPES_STR.CONTEXT ] -/** @enum {('FILE'|'CD ROM'|'BLOCK'|'RBD')} Disk type */ +/** @enum {string} Disk type */ +export const DISK_TYPES_STR = { + FILE: 'FILE', + CDROM: 'CDROM', + BLOCK: 'BLOCK', + RBD: 'RBD', + RBD_CDROM: 'RBD CDROM', + GLUSTER: 'GLUSTER', + GLUSTER_CDROM: 'GLUSTER CDROM', + SHEEPDOG: 'SHEEPDOG', + SHEEPDOG_CDROM: 'SHEEPDOG CDROM', + ISCSI: 'ISCII' +} +/** @enum {DISK_TYPES_STR[]} Return the string representation of a Disk type */ export const DISK_TYPES = [ - 'FILE', - 'CD ROM', - 'BLOCK', - 'RBD' + DISK_TYPES_STR.FILE, + DISK_TYPES_STR.CDROM, + DISK_TYPES_STR.BLOCK, + DISK_TYPES_STR.RBD, + DISK_TYPES_STR.RBD_CDROM, + DISK_TYPES_STR.GLUSTER, + DISK_TYPES_STR.GLUSTER_CDROM, + DISK_TYPES_STR.SHEEPDOG, + DISK_TYPES_STR.SHEEPDOG_CDROM, + DISK_TYPES_STR.ISCSI ] /** @type {STATES.StateInfo[]} Image states */ diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js index bd6c3c7a2e..2ec55b8c17 100644 --- a/src/fireedge/src/client/constants/index.js +++ b/src/fireedge/src/client/constants/index.js @@ -31,6 +31,7 @@ export const WEBSOCKET_URL = `${APP_URL}/websockets` export const STATIC_FILES_URL = `${APP_URL}/client/assets` export const IMAGES_URL = `${STATIC_FILES_URL}/images` +export const LOGO_IMAGES_URL = `${IMAGES_URL}/logos` export const PROVIDER_IMAGES_URL = `${IMAGES_URL}/providers` export const PROVISION_IMAGES_URL = `${IMAGES_URL}/provisions` export const DEFAULT_IMAGE = `${IMAGES_URL}/default.webp` diff --git a/src/fireedge/src/client/features/One/utils.js b/src/fireedge/src/client/features/One/utils.js index d8b34bb417..5c878d6ae3 100644 --- a/src/fireedge/src/client/features/One/utils.js +++ b/src/fireedge/src/client/features/One/utils.js @@ -61,7 +61,7 @@ export const updateResourceList = (currentList, value) => { // update if exists in current list, if not add it to list const updatedList = currentItem ? currentList?.map(item => item?.ID === id ? value : item) - : [value, currentList] + : [value, ...currentList] return updatedList } diff --git a/src/fireedge/src/client/features/One/vmTemplate/actions.js b/src/fireedge/src/client/features/One/vmTemplate/actions.js index 333d153005..2813e4cf48 100644 --- a/src/fireedge/src/client/features/One/vmTemplate/actions.js +++ b/src/fireedge/src/client/features/One/vmTemplate/actions.js @@ -40,4 +40,4 @@ export const changePermissions = createAction(`${TEMPLATE}/chmod`, vmTemplateSer export const changeOwnership = createAction(`${TEMPLATE}/chown`, vmTemplateService.changeOwnership) export const rename = createAction(`${TEMPLATE}/rename`, vmTemplateService.rename) export const lock = createAction(`${TEMPLATE}/lock`, vmTemplateService.lock) -export const unlock = createAction(`${TEMPLATE}/unlock`, vmTemplateService.lock) +export const unlock = createAction(`${TEMPLATE}/unlock`, vmTemplateService.unlock) diff --git a/src/fireedge/src/client/hooks/useListForm.js b/src/fireedge/src/client/hooks/useListForm.js index 42973b503e..f09b78d6d3 100644 --- a/src/fireedge/src/client/hooks/useListForm.js +++ b/src/fireedge/src/client/hooks/useListForm.js @@ -61,7 +61,7 @@ import { set } from 'client/utils' * @property {SimpleCallback} handleSelect - Add an item to data form list * @property {SimpleCallback} handleClone - Clones an item and change two attributes * @property {SimpleCallback} handleEdit - Find the element by id and set value to editing state - * @property {function(newValues, id)} handleSave - Saves the data from editing state + * @property {SaveCallback} handleSave - Saves the data from editing state */ // ---------------------------------------------------------- @@ -182,7 +182,7 @@ const useListForm = ({ const index = EXISTS_INDEX(itemIndex) ? itemIndex : list.length const newList = Object.assign([], [...list], - { [index]: getItemId(values) ? values : addItemId(values, id, itemIndex) } + { [index]: getItemId(values) ? values : addItemId(values, id, index) } ) handleSetList(newList) diff --git a/src/fireedge/src/client/models/Image.js b/src/fireedge/src/client/models/Image.js index fa123f79ab..a20903cd8c 100644 --- a/src/fireedge/src/client/models/Image.js +++ b/src/fireedge/src/client/models/Image.js @@ -22,7 +22,8 @@ import { IMAGE_TYPES, DISK_TYPES, IMAGE_STATES, StateInfo } from 'client/constan * @param {number|string} image.TYPE - Type numeric code * @returns {IMAGE_TYPES} - Image type */ -export const getType = ({ TYPE } = {}) => IMAGE_TYPES[+TYPE] +export const getType = ({ TYPE } = {}) => + isNaN(+TYPE) ? TYPE : IMAGE_TYPES[+TYPE] /** * Returns the image state. @@ -40,4 +41,5 @@ export const getState = ({ STATE } = {}) => IMAGE_STATES[+STATE] * @param {number|string} image.DISK_TYPE - Disk type numeric code * @returns {DISK_TYPES} - Disk type */ -export const getDiskType = ({ DISK_TYPE } = {}) => DISK_TYPES[+DISK_TYPE] +export const getDiskType = ({ DISK_TYPE } = {}) => + isNaN(+DISK_TYPE) ? DISK_TYPE : DISK_TYPES[+DISK_TYPE] diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js index 905e63c4da..faab65678f 100644 --- a/src/fireedge/src/client/utils/schema.js +++ b/src/fireedge/src/client/utils/schema.js @@ -111,7 +111,7 @@ import { INPUT_TYPES } from 'client/constants' /** * @typedef {object} StepsForm * @property {Step[]} steps - Steps - * @property {BaseSchema} resolver - Schema + * @property {BaseSchema|function():BaseSchema} resolver - Schema * @property {object} defaultValues - Default values */ @@ -347,8 +347,13 @@ export const createForm = (schema, fields, extraParams = {}) => const schemaCallback = typeof schema === 'function' ? schema(props) : schema const fieldsCallback = typeof fields === 'function' ? fields(props) : fields + const defaultTransformInitialValue = (values, schema) => + schema.cast(values, { stripUnknown: true }) + + const { transformInitialValue = defaultTransformInitialValue } = extraParams + const defaultValues = initialValues - ? schemaCallback.cast(initialValues, { stripUnknown: true }) + ? transformInitialValue(initialValues, schemaCallback) : schemaCallback.default() return {