@@ -22,6 +22,7 @@ import globalImageStore from 'stores/glance/image';
2222import globalInstanceSnapshotStore from 'stores/glance/instance-snapshot' ;
2323import globalVolumeTypeStore from 'stores/cinder/volume-type' ;
2424import globalAvailabilityZoneStore from 'stores/nova/zone' ;
25+ import globalLeaseStore from 'stores/blazar/lease' ;
2526import { VolumeStore } from 'stores/cinder/volume' ;
2627import {
2728 canImageCreateInstance ,
@@ -34,6 +35,11 @@ import {
3435import Base from 'components/Form' ;
3536import InstanceVolume from 'components/FormItem/InstanceVolume' ;
3637import { isGpuCategory } from 'resources/nova/flavor' ;
38+ import {
39+ isBlazarInternalAvailabilityZone ,
40+ getUsableLeaseReservations ,
41+ RESERVATION_TYPES ,
42+ } from 'resources/blazar/reservation' ;
3743import {
3844 volumeTypes ,
3945 getDiskInfo ,
@@ -48,12 +54,14 @@ export class BaseStep extends Base {
4854 this . volumeStore = new VolumeStore ( ) ;
4955 this . volumeTypeStore = globalVolumeTypeStore ;
5056 this . instanceSnapshotStore = globalInstanceSnapshotStore ;
57+ this . leaseStore = globalLeaseStore ;
5158 this . getAvailZones ( ) ;
5259 this . getImages ( ) ;
5360 this . getVolumeTypes ( ) ;
5461 this . getVolumes ( ) ;
5562 this . getInstanceSnapshots ( ) ;
5663 this . initSourceChange ( ) ;
64+ this . getHostReservations ( ) ;
5765 }
5866
5967 get title ( ) {
@@ -69,7 +77,7 @@ export class BaseStep extends Base {
6977 }
7078
7179 get defaultValue ( ) {
72- const { volume, snapshot } = this . locationParams ;
80+ const { flavor , volume, snapshot } = this . locationParams ;
7381 let source = this . imageSourceType ;
7482 if ( volume ) {
7583 source = this . volumeSourceType ;
@@ -85,12 +93,28 @@ export class BaseStep extends Base {
8593 if ( source . value === 'image' ) {
8694 values . bootFromVolume = true ;
8795 }
96+ if ( flavor ) {
97+ values . flavor = {
98+ selectedRowKeys : [ flavor ] ,
99+ selectedRows : [ ] ,
100+ } ;
101+ }
102+ const { reservation } = this . locationParams ;
103+ if ( reservation ) {
104+ values . hostReservation = {
105+ selectedRowKeys : [ reservation ] ,
106+ selectedRows : this . hostReservations . filter (
107+ ( it ) => it . id === reservation
108+ ) ,
109+ } ;
110+ }
88111 return values ;
89112 }
90113
91114 get availableZones ( ) {
92115 return ( globalAvailabilityZoneStore . list . data || [ ] )
93116 . filter ( ( it ) => it . zoneState . available )
117+ . filter ( ( it ) => ! isBlazarInternalAvailabilityZone ( it . zoneName ) )
94118 . map ( ( it ) => ( {
95119 value : it . zoneName ,
96120 label : it . zoneName ,
@@ -210,6 +234,56 @@ export class BaseStep extends Base {
210234 }
211235 }
212236
237+ get enableBlazar ( ) {
238+ return this . props . rootStore . checkEndpoint ( 'blazar' ) ;
239+ }
240+
241+ get hostReservations ( ) {
242+ if ( ! this . enableBlazar ) {
243+ return [ ] ;
244+ }
245+ const leases = this . leaseStore . list . data || [ ] ;
246+ return leases . reduce ( ( results , lease ) => {
247+ const reservations = getUsableLeaseReservations ( lease )
248+ . filter ( ( r ) => r . resource_type === RESERVATION_TYPES . HOST )
249+ . map ( ( r ) => ( { ...r , key : r . id , name : r . id } ) ) ;
250+ return [ ...results , ...reservations ] ;
251+ } , [ ] ) ;
252+ }
253+
254+ get hasHostReservationSelected ( ) {
255+ // Only true when arriving from the lease detail page (?reservation=<id>).
256+ const { reservation } = this . locationParams ;
257+ return ! ! reservation ;
258+ }
259+
260+ get hostReservationFormItem ( ) {
261+ const { reservation } = this . locationParams ;
262+
263+ // Only show when arriving from the lease detail page (?reservation=<id>).
264+ // In the normal create instance flow there is no host reservation input.
265+ if ( ! reservation ) {
266+ return { name : 'hostReservation' , hidden : true } ;
267+ }
268+
269+ // Show as a read-only label — same pattern as the Project field.
270+ const displayText = reservation ;
271+ return {
272+ name : 'hostReservation' ,
273+ label : t ( 'Host Reservation' ) ,
274+ type : 'label' ,
275+ content : displayText ,
276+ } ;
277+ }
278+
279+ async getHostReservations ( ) {
280+ if ( ! this . enableBlazar ) {
281+ return ;
282+ }
283+ await this . leaseStore . fetchList ( ) ;
284+ this . updateDefaultValue ( ) ;
285+ }
286+
213287 async getImages ( ) {
214288 const { volume, image, snapshot } = this . locationParams ;
215289 if ( volume || snapshot ) {
@@ -791,12 +865,14 @@ export class BaseStep extends Base {
791865 type : 'select' ,
792866 placeholder : t ( 'Please select' ) ,
793867 isWrappedValue : true ,
794- required : true ,
868+ required : ! this . hasHostReservationSelected ,
869+ hidden : this . hasHostReservationSelected ,
795870 options : this . availableZones ,
796871 tip : t (
797872 'Availability zone refers to a physical area where power and network are independent of each other in the same area. In the same region, the availability zone and the availability zone can communicate with each other in the intranet, and the available zones can achieve fault isolation.'
798873 ) ,
799874 } ,
875+ this . hostReservationFormItem ,
800876 {
801877 type : 'divider' ,
802878 } ,
0 commit comments