From 33120a005a777b719b28c87a38b342cb25d3b20b Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Sat, 5 Jan 2019 19:26:17 +0800 Subject: [PATCH] add network merge split --- pkg/cloudcommon/db/db_dispatcher.go | 2 +- pkg/cloudcommon/db/namevalidator.go | 2 +- pkg/cloudcommon/db/opslog.go | 5 +- pkg/compute/models/networks.go | 278 ++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+), 4 deletions(-) diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index 1b03d0fe48..3430c0cea6 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -788,7 +788,7 @@ func doCreateItem(manager IModelManager, ctx context.Context, userCred mcclient. } else { name, _ := data.GetString("name") if len(name) > 0 { - err = newNameValidator(manager, ownerProjId, name) + err = NewNameValidator(manager, ownerProjId, name) if err != nil { return nil, err } diff --git a/pkg/cloudcommon/db/namevalidator.go b/pkg/cloudcommon/db/namevalidator.go index bc9f62fd0b..ca4fa056e4 100644 --- a/pkg/cloudcommon/db/namevalidator.go +++ b/pkg/cloudcommon/db/namevalidator.go @@ -17,7 +17,7 @@ func isNameUnique(manager IModelManager, owner string, name string) bool { return q.Count() == 0 } -func newNameValidator(manager IModelManager, ownerProjId string, name string) error { +func NewNameValidator(manager IModelManager, ownerProjId string, name string) error { err := manager.ValidateName(name) if err != nil { return err diff --git a/pkg/cloudcommon/db/opslog.go b/pkg/cloudcommon/db/opslog.go index a1e703fe9a..c5aa149d02 100644 --- a/pkg/cloudcommon/db/opslog.go +++ b/pkg/cloudcommon/db/opslog.go @@ -50,6 +50,9 @@ const ( ACT_MIGRATE = "migrate" ACT_MIGRATE_FAIL = "migrate_fail" + ACT_SPLIT = "net_split" + ACT_MERGE = "net_merge" + ACT_SAVING = "saving" ACT_SAVE = "save" ACT_SAVE_FAIL = "save_fail" @@ -132,8 +135,6 @@ const ( ACT_SYNC_CLOUD_SERVER = "sync_cloud_server" ACT_SYNC_CLOUD_EIP = "sync_cloud_eip" - ACT_SPLIT = "net_split" - ACT_PENDING_DELETE = "pending_delete" ACT_CANCEL_DELETE = "cancel_delete" diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index c43b5b5255..6702ed4120 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -18,12 +18,14 @@ import ( "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/util/logclient" ) const ( @@ -1480,3 +1482,279 @@ func (self *SNetwork) PerformPurge(ctx context.Context, userCred mcclient.TokenC err = self.RealDelete(ctx, userCred) return nil, err } + +func (self *SNetwork) AllowPerformSplit(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "split") +} + +func (self *SNetwork) AllowPerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "merge") +} + +func (self *SNetwork) PerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + target, err := data.GetString("target") + if err != nil { + return nil, httperrors.NewMissingParameterError("target") + } + iNet, err := NetworkManager.FetchByIdOrName(userCred, target) + if err == sql.ErrNoRows { + err = httperrors.NewNotFoundError("Network %s not found", target) + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } else if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + + net := iNet.(*SNetwork) + if net == nil { + err = fmt.Errorf("Network is nil") + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + if self.WireId != net.WireId || self.GuestGateway != net.GuestGateway { + err = httperrors.NewInputParameterError("Invalid Target Network: %s", target) + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + + var startIp, endIp string + ipNE, _ := netutils.NewIPV4Addr(net.GuestIpEnd) + ipNS, _ := netutils.NewIPV4Addr(net.GuestIpStart) + ipSS, _ := netutils.NewIPV4Addr(self.GuestIpStart) + ipSE, _ := netutils.NewIPV4Addr(self.GuestIpEnd) + + if ipNE.StepUp() == ipSS { + startIp, endIp = net.GuestIpStart, self.GuestIpEnd + } else if ipSE == ipNS { + startIp, endIp = self.GuestIpStart, net.GuestIpEnd + } else { + note := "Incontinuity Network for %s and %s" + logclient.AddActionLog(self, logclient.ACT_MERGE, + fmt.Sprintf(note, self.Name, net.Name), userCred, false) + return nil, httperrors.NewBadRequestError(note, self.Name, net.Name) + } + + lockman.LockClass(ctx, NetworkManager, userCred.GetProjectId()) + defer lockman.ReleaseClass(ctx, NetworkManager, userCred.GetProjectId()) + + _, err = NetworkManager.TableSpec().Update(net, func() error { + net.GuestIpStart = startIp + net.GuestIpEnd = endIp + return nil + }) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + + guestnetworks := make([]SGuestnetwork, 0) + err = GuestnetworkManager.Query().Equals("network_id", self.Id).All(&guestnetworks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + for _, gn := range guestnetworks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if self.isAddressInRange(addr) { + GuestnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = net.Id + return nil + }) + } + } + + hostnetworks := make([]SHostnetwork, 0) + err = HostnetworkManager.Query().Equals("network_id", self.Id).All(&hostnetworks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + for _, gn := range hostnetworks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if self.isAddressInRange(addr) { + HostnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = net.Id + return nil + }) + } + } + + reservedips := make([]SReservedip, 0) + err = ReservedipManager.Query().Equals("network_id", self.Id).All(&reservedips) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + for _, gn := range reservedips { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if self.isAddressInRange(addr) { + ReservedipManager.TableSpec().Update(gn, func() error { + gn.NetworkId = net.Id + return nil + }) + } + } + + groupnetwroks := make([]SGroupnetwork, 0) + err = GroupnetworkManager.Query().Equals("network_id", self.Id).All(&groupnetwroks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_MERGE, err.Error(), userCred, false) + return nil, err + } + for _, gn := range groupnetwroks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if self.isAddressInRange(addr) { + GroupnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = net.Id + return nil + }) + } + } + + note := map[string]string{"start_ip": startIp, "end_ip": endIp} + db.OpsLog.LogEvent(self, db.ACT_MERGE, note, userCred) + logclient.AddActionLog(self, logclient.ACT_MERGE, note, userCred, true) + + if err = self.RealDelete(ctx, userCred); err != nil { + return nil, err + } + note = map[string]string{"network": self.Id} + db.OpsLog.LogEvent(self, db.ACT_DELETE, note, userCred) + logclient.AddActionLog(self, logclient.ACT_DELETE, note, userCred, true) + return nil, nil +} + +func (self *SNetwork) PerformSplit(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + splitIp, err := data.GetString("split_ip") + if err != nil { + return nil, httperrors.NewMissingParameterError("split_ip") + } + name, _ := data.GetString("name") + + if !regutils.MatchIPAddr(splitIp) { + return nil, httperrors.NewInputParameterError("Invalid IP %s", splitIp) + } + if splitIp == self.GuestIpStart { + return nil, httperrors.NewInputParameterError("Split IP %s is the start ip", splitIp) + } + + iSplitIp, err := netutils.NewIPV4Addr(splitIp) + if err != nil { + return nil, err + } + if !self.isAddressInRange(iSplitIp) { + return nil, httperrors.NewInputParameterError("Split IP %s out of range", splitIp) + } + + lockman.LockClass(ctx, NetworkManager, userCred.GetProjectId()) + defer lockman.ReleaseClass(ctx, NetworkManager, userCred.GetProjectId()) + + if len(name) > 0 { + if err := db.NewNameValidator(NetworkManager, userCred.GetProjectId(), name); err != nil { + return nil, httperrors.NewInputParameterError("Duplicate name %s", name) + } else { + name = db.GenerateName(NetworkManager, userCred.GetProjectId(), fmt.Sprintf("%s#", self.Name)) + } + } + + network := &SNetwork{} + network.Name = name + network.GuestIpStart = splitIp + network.GuestIpEnd = self.GuestIpEnd + network.GuestIpMask = self.GuestIpMask + network.GuestGateway = self.GuestGateway + network.GuestDns = self.GuestDns + network.GuestDhcp = self.GuestDhcp + network.GuestDomain = self.GuestDomain + network.VlanId = self.VlanId + network.WireId = self.WireId + network.ServerType = self.ServerType + network.IsPublic = self.IsPublic + network.Status = self.Status + network.ProjectId = self.ProjectId + // network.UserId = self.UserId + network.IsSystem = self.IsSystem + network.Description = self.Description + + err = NetworkManager.TableSpec().Insert(network) + if err != nil { + return nil, err + } + network.SetModelManager(NetworkManager) + + self.GetModelManager().TableSpec().Update(self, func() error { + self.GuestIpEnd = iSplitIp.StepUp().String() + return nil + }) + + guestnetworks := make([]SGuestnetwork, 0) + err = GuestnetworkManager.Query().Equals("network_id", self.Id).All(&guestnetworks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_SPLIT, err.Error(), userCred, false) + return nil, err + } + for _, gn := range guestnetworks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if network.isAddressInRange(addr) { + GuestnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = network.Id + return nil + }) + } + } + + hostnetworks := make([]SHostnetwork, 0) + err = HostnetworkManager.Query().Equals("network_id", self.Id).All(&hostnetworks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_SPLIT, err.Error(), userCred, false) + return nil, err + } + for _, gn := range hostnetworks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if network.isAddressInRange(addr) { + HostnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = network.Id + return nil + }) + } + } + + reservedips := make([]SReservedip, 0) + err = ReservedipManager.Query().Equals("network_id", self.Id).All(&reservedips) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_SPLIT, err.Error(), userCred, false) + return nil, err + } + for _, gn := range reservedips { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if network.isAddressInRange(addr) { + ReservedipManager.TableSpec().Update(gn, func() error { + gn.NetworkId = network.Id + return nil + }) + } + } + + groupnetworks := make([]SGroupnetwork, 0) + err = GroupnetworkManager.Query().Equals("network_id", self.Id).All(&groupnetworks) + if err != nil { + logclient.AddActionLog(self, logclient.ACT_SPLIT, err.Error(), userCred, false) + return nil, err + } + for _, gn := range groupnetworks { + addr, _ := netutils.NewIPV4Addr(gn.IpAddr) + if network.isAddressInRange(addr) { + GroupnetworkManager.TableSpec().Update(gn, func() error { + gn.NetworkId = network.Id + return nil + }) + } + } + + note := map[string]string{"split_ip": splitIp, "end_ip": network.GuestIpEnd} + db.OpsLog.LogEvent(self, db.ACT_SPLIT, note, userCred) + logclient.AddActionLog(self, logclient.ACT_SPLIT, note, userCred, true) + db.OpsLog.LogEvent(network, db.ACT_CREATE, map[string]string{"network": self.Id}, userCred) + return nil, nil +}