@@ -28,6 +28,7 @@ export default function HcpCliAssistant() {
28
28
nodeAZ : "" ,
29
29
nodeImageName : "" ,
30
30
dnsNameservers : "" ,
31
+ additionalPorts : "" ,
31
32
} ) ;
32
33
33
34
const handleChange = ( e ) => {
@@ -44,6 +45,16 @@ export default function HcpCliAssistant() {
44
45
return form . name . trim ( ) !== "" && form . baseDomain . trim ( ) !== "" && form . nodePoolReplicas . trim ( ) !== "" ;
45
46
case 1 :
46
47
return form . osCloudSet || form . openstackCredentialsFile . trim ( ) !== "" ;
48
+ case 2 :
49
+ if ( form . additionalPorts ) {
50
+ try {
51
+ const ports = JSON . parse ( form . additionalPorts ) ;
52
+ return ports . every ( ( port ) => port . networkId . trim ( ) !== "" ) ;
53
+ } catch ( e ) {
54
+ return false ;
55
+ }
56
+ }
57
+ return true ;
47
58
case 3 :
48
59
return form . nodeFlavor . trim ( ) !== "" ;
49
60
default :
@@ -97,6 +108,28 @@ export default function HcpCliAssistant() {
97
108
--openstack-node-image-name ${ form . nodeImageName } ` ;
98
109
}
99
110
111
+ if ( form . additionalPorts ) {
112
+ const ports = JSON . parse ( form . additionalPorts ) ;
113
+ ports . forEach ( ( port , index ) => {
114
+ let portConfig = `--openstack-node-additional-port=network-id:${ port . networkId } ` ;
115
+
116
+ if ( port . vnicType ) {
117
+ portConfig += `,vnic-type:${ port . vnicType } ` ;
118
+ }
119
+
120
+ if ( port . addressPairs ) {
121
+ portConfig += `,address-pairs:${ port . addressPairs } ` ;
122
+ }
123
+
124
+ if ( port . disablePortSecurity ) {
125
+ portConfig += `,disable-port-security:true` ;
126
+ }
127
+
128
+ cmd += ` \
129
+ ${ portConfig } ` ;
130
+ } ) ;
131
+ }
132
+
100
133
cmd = cmd . replace ( / \s + / g, ' ' ) . trim ( ) ;
101
134
102
135
return cmd ;
@@ -133,16 +166,129 @@ export default function HcpCliAssistant() {
133
166
</ div >
134
167
) : (
135
168
< div className = "space-y-4" >
136
- { step === 0 && < > < input type = "text" name = "name" placeholder = "Cluster Name" value = { form . name } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "text" name = "baseDomain" placeholder = "Base Domain" value = { form . baseDomain } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "number" name = "nodePoolReplicas" placeholder = "Node Pool Replicas" value = { form . nodePoolReplicas } onChange = { handleChange } className = "w-full p-2 border rounded" required /> </ > }
169
+ { step === 0 && < > < input type = "text" name = "name" placeholder = "Cluster Name (required). Example: test " value = { form . name } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "text" name = "baseDomain" placeholder = "Base Domain (required). Example: mydomain.com " value = { form . baseDomain } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "number" name = "nodePoolReplicas" placeholder = "Node Pool Replicas (required) " value = { form . nodePoolReplicas } onChange = { handleChange } className = "w-full p-2 border rounded" required /> </ > }
137
170
{ step === 1 && < > < label className = "block" > < input type = "checkbox" name = "osCloudSet" checked = { form . osCloudSet } onChange = { handleChange } className = "mr-2" /> OS_CLOUD is set in the environment</ label > { ! form . osCloudSet && (
138
171
< >
139
- < input type = "text" name = "openstackCaCertFile" placeholder = "OpenStack CA Certificate File (optional)" value = { form . openstackCaCertFile } onChange = { handleChange } className = "w-full p-2 border rounded" />
172
+ < input type = "text" name = "openstackCredentialsFile" placeholder = "OpenStack Credentials File (optional). Example: /tmp/clouds.yaml" value = { form . openstackCredentialsFile } onChange = { handleChange } className = "w-full p-2 border rounded" />
173
+ </ >
174
+ ) }
175
+ < input type = "text" name = "openstackCloud" placeholder = "OpenStack Cloud (optional). Default: openstack" value = { form . openstackCloud } onChange = { handleChange } className = "w-full p-2 border rounded" />
176
+ < input type = "text" name = "openstackCaCertFile" placeholder = "OpenStack CA Certificate File (optional). Example: /tmp/ca.cert" value = { form . openstackCaCertFile } onChange = { handleChange } className = "w-full p-2 border rounded" /> </ > }
177
+ { step === 2 && (
178
+ < >
179
+ < input type = "text" name = "externalNetworkId" placeholder = "External Network ID (optional). Example: 64f629fd-f75b-4e66-96ad-94f6f2125ba4" value = { form . externalNetworkId } onChange = { handleChange } className = "w-full p-2 border rounded" />
180
+ < input type = "text" name = "ingressFloatingIp" placeholder = "Ingress Floating IP (optional). Example: 192.168.100.7" value = { form . ingressFloatingIp } onChange = { handleChange } className = "w-full p-2 border rounded" />
181
+ < input type = "text" name = "dnsNameservers" placeholder = "DNS Nameservers (optional). Example: 1.1.1.1,8.8.8.8" value = { form . dnsNameservers } onChange = { handleChange } className = "w-full p-2 border rounded" />
182
+
183
+ < div className = "mt-4" >
184
+ < h3 className = "font-semibold mb-2" > Additional Nodepool Ports (optional)</ h3 >
185
+ { form . additionalPorts && JSON . parse ( form . additionalPorts ) . map ( ( port , index ) => (
186
+ < div key = { index } className = "p-3 border rounded mb-2" >
187
+ < div className = "flex items-center justify-between mb-2" >
188
+ < span className = "font-medium" > Port { index + 1 } </ span >
189
+ < button
190
+ onClick = { ( ) => {
191
+ const ports = JSON . parse ( form . additionalPorts ) ;
192
+ ports . splice ( index , 1 ) ;
193
+ setForm ( {
194
+ ...form ,
195
+ additionalPorts : JSON . stringify ( ports )
196
+ } ) ;
197
+ } }
198
+ className = "px-2 py-1 bg-red-500 text-white rounded"
199
+ >
200
+ Remove
201
+ </ button >
202
+ </ div >
203
+ < div className = "grid grid-cols-2 gap-2" >
204
+ < div >
205
+ < label className = "block text-sm" > Network ID (required)</ label >
206
+ < input
207
+ type = "text"
208
+ value = { port . networkId || "" }
209
+ onChange = { ( e ) => {
210
+ const ports = JSON . parse ( form . additionalPorts ) ;
211
+ ports [ index ] . networkId = e . target . value ;
212
+ setForm ( {
213
+ ...form ,
214
+ additionalPorts : JSON . stringify ( ports )
215
+ } ) ;
216
+ } }
217
+ className = "w-full p-2 border rounded"
218
+ required
219
+ />
220
+ </ div >
221
+ < div >
222
+ < label className = "block text-sm" > VNIC Type (optional)</ label >
223
+ < input
224
+ type = "text"
225
+ value = { port . vnicType || "" }
226
+ onChange = { ( e ) => {
227
+ const ports = JSON . parse ( form . additionalPorts ) ;
228
+ ports [ index ] . vnicType = e . target . value ;
229
+ setForm ( {
230
+ ...form ,
231
+ additionalPorts : JSON . stringify ( ports )
232
+ } ) ;
233
+ } }
234
+ className = "w-full p-2 border rounded"
235
+ />
236
+ </ div >
237
+ < div >
238
+ < label className = "block text-sm" > Address Pairs (optional)</ label >
239
+ < input
240
+ type = "text"
241
+ value = { port . addressPairs || "" }
242
+ onChange = { ( e ) => {
243
+ const ports = JSON . parse ( form . additionalPorts ) ;
244
+ ports [ index ] . addressPairs = e . target . value ;
245
+ setForm ( {
246
+ ...form ,
247
+ additionalPorts : JSON . stringify ( ports )
248
+ } ) ;
249
+ } }
250
+ className = "w-full p-2 border rounded"
251
+ placeholder = "ip_address=mac_address,ip_address=mac_address"
252
+ />
253
+ </ div >
254
+ < div className = "flex items-center" >
255
+ < label className = "flex items-center text-sm" >
256
+ < input
257
+ type = "checkbox"
258
+ checked = { port . disablePortSecurity || false }
259
+ onChange = { ( e ) => {
260
+ const ports = JSON . parse ( form . additionalPorts ) ;
261
+ ports [ index ] . disablePortSecurity = e . target . checked ;
262
+ setForm ( {
263
+ ...form ,
264
+ additionalPorts : JSON . stringify ( ports )
265
+ } ) ;
266
+ } }
267
+ className = "mr-2"
268
+ />
269
+ Disable Port Security
270
+ </ label >
271
+ </ div >
272
+ </ div >
273
+ </ div >
274
+ ) ) }
275
+ < button
276
+ onClick = { ( ) => {
277
+ const ports = form . additionalPorts ? JSON . parse ( form . additionalPorts ) : [ ] ;
278
+ ports . push ( { networkId : "" } ) ;
279
+ setForm ( {
280
+ ...form ,
281
+ additionalPorts : JSON . stringify ( ports )
282
+ } ) ;
283
+ } }
284
+ className = "mt-2 px-3 py-2 bg-green-500 text-white rounded"
285
+ >
286
+ Add Port
287
+ </ button >
288
+ </ div >
140
289
</ >
141
290
) }
142
- < input type = "text" name = "openstackCloud" placeholder = "OpenStack Cloud (default: openstack)" value = { form . openstackCloud } onChange = { handleChange } className = "w-full p-2 border rounded" />
143
- < input type = "text" name = "openstackCaCertFile" placeholder = "OpenStack CA Certificate File (optional)" value = { form . openstackCaCertFile } onChange = { handleChange } className = "w-full p-2 border rounded" /> </ > }
144
- { step === 2 && < > < input type = "text" name = "externalNetworkId" placeholder = "External Network ID" value = { form . externalNetworkId } onChange = { handleChange } className = "w-full p-2 border rounded" /> < input type = "text" name = "ingressFloatingIp" placeholder = "Ingress Floating IP" value = { form . ingressFloatingIp } onChange = { handleChange } className = "w-full p-2 border rounded" /> < input type = "text" name = "dnsNameservers" placeholder = "DNS Nameservers (comma-separated)" value = { form . dnsNameservers } onChange = { handleChange } className = "w-full p-2 border rounded" /> </ > }
145
- { step === 3 && < > < input type = "text" name = "nodeFlavor" placeholder = "OpenStack Flavor name for the Nodepool" value = { form . nodeFlavor } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "text" name = "nodeAZ" placeholder = "Nova Availability Zone (optional)" value = { form . nodeAZ } onChange = { handleChange } className = "w-full p-2 border rounded" /> < input type = "text" name = "nodeImageName" placeholder = "OpenStack Glance Image Name (optional)" value = { form . nodeImageName } onChange = { handleChange } className = "w-full p-2 border rounded" /> </ > }
291
+ { step === 3 && < > < input type = "text" name = "nodeFlavor" placeholder = "Flavor name for the Nodepool (Required)" value = { form . nodeFlavor } onChange = { handleChange } className = "w-full p-2 border rounded" required /> < input type = "text" name = "nodeAZ" placeholder = "Nova Availability Zone (optional)" value = { form . nodeAZ } onChange = { handleChange } className = "w-full p-2 border rounded" /> < input type = "text" name = "nodeImageName" placeholder = "Glance Image Name (optional)" value = { form . nodeImageName } onChange = { handleChange } className = "w-full p-2 border rounded" /> </ > }
146
292
</ div >
147
293
) }
148
294
0 commit comments