diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 654333a..278e72f 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -27,7 +27,8 @@ type Cluster struct { Domain string `json:"domain,omitempty" yaml:"domain,omitempty"` CephFSID uuid.UUID `json:"ceph_fsid,omitempty" yaml:"ceph_fsid,omitempty"` - Zones []*Zone `json:"zones,omitempty" yaml:"zones,omitempty"` + Regions []Region `json:",omitempty" yaml:",omitempty"` + Zones []*Zone `json:",omitempty" yaml:",omitempty"` } // revive:enable:line-length-limit diff --git a/pkg/cluster/cluster_import.go b/pkg/cluster/cluster_import.go index ac597da..697e476 100644 --- a/pkg/cluster/cluster_import.go +++ b/pkg/cluster/cluster_import.go @@ -14,6 +14,7 @@ func (m *Cluster) init(opts *ScanOptions) error { m.scanZoneIDs, m.scanSort, m.scanGateways, + m.initRegions, } { if err := fn(opts); err != nil { return err diff --git a/pkg/cluster/regions.go b/pkg/cluster/regions.go new file mode 100644 index 0000000..134cb2e --- /dev/null +++ b/pkg/cluster/regions.go @@ -0,0 +1,88 @@ +package cluster + +// Region represents a group of zones geographically related +type Region struct { + m *Cluster + zones []*Zone + + Name string + Regions []string `json:",omitempty" yaml:",omitempty"` +} + +func (m *Cluster) initRegions(_ *ScanOptions) error { + regions := make(map[string][]*Zone) + + // first regions defined by zones + m.ForEachZone(func(z *Zone) bool { + for _, region := range z.Regions { + regions[region] = append(regions[region], z) + } + + return false + }) + + // bind first level regions and their zones + for name, zones := range regions { + m.syncRegions(name, zones...) + } + + // and combine zones to produce larger regions + for i := range m.Regions { + r := &m.Regions[i] + m.finishRegion(r) + } + + return nil +} + +func (m *Cluster) syncRegions(name string, zones ...*Zone) { + for _, r := range m.Regions { + if r.Name == name { + // found + r.m = m + r.zones = zones + return + } + } + + // new + m.Regions = append(m.Regions, Region{ + m: m, + zones: zones, + Name: name, + }) +} + +func (m *Cluster) finishRegion(r *Region) { + if r.m != nil { + // ready + return + } + + r.m = m + sub := []string{} + for _, name := range r.Regions { + r2, ok := m.getRegion(name) + if !ok { + m.warn(nil).WithField("region", name).Print("unknown region") + continue + } + + sub = append(sub, r2.Name) + r.zones = append(r.zones, r2.zones...) + } + r.Regions = sub +} + +func (m *Cluster) getRegion(name string) (*Region, bool) { + for i := range m.Regions { + r := &m.Regions[i] + + if name == r.Name { + m.finishRegion(r) + return r, true + } + } + + return nil, false +} diff --git a/pkg/cluster/zones.go b/pkg/cluster/zones.go index 59898b9..c6f1582 100644 --- a/pkg/cluster/zones.go +++ b/pkg/cluster/zones.go @@ -19,8 +19,9 @@ type Zone struct { zones *Cluster logger `json:"-" yaml:"-"` - ID int - Name string + ID int + Name string + Regions []string `json:",omitempty" yaml:",omitempty"` Machines }