-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
centralize resource allocation #23
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,8 +24,6 @@ import ( | |
|
||
"github.com/contiv/libovsdb" | ||
"github.com/contiv/netplugin/core" | ||
"github.com/contiv/netplugin/gstate" | ||
"github.com/contiv/netplugin/netutils" | ||
) | ||
|
||
// implements the NetworkDriver and EndpointDriver interface for an vlan based | ||
|
@@ -238,7 +236,7 @@ func (d *OvsDriver) createDeletePort(portName, intfName, intfType, id string, | |
} | ||
|
||
if intfOptions != nil { | ||
log.Printf("using options %v \n", intfOptions) | ||
// log.Printf("using options %v \n", intfOptions) | ||
intf["options"], err = libovsdb.NewOvsMap(intfOptions) | ||
if err != nil { | ||
log.Printf("error '%s' creating options from %v \n", err, intfOptions) | ||
|
@@ -426,169 +424,35 @@ func (d *OvsDriver) Deinit() { | |
} | ||
|
||
func (d *OvsDriver) CreateNetwork(id string) error { | ||
var err error | ||
var extPktTag, pktTag uint | ||
var subnetLen uint | ||
var subnetIp string | ||
var gOper gstate.Oper | ||
|
||
operNwState := OvsOperNetworkState{StateDriver: d.stateDriver} | ||
err = operNwState.Read(id) | ||
if err == nil { | ||
return err | ||
} | ||
|
||
cfgNetState := OvsCfgNetworkState{StateDriver: d.stateDriver} | ||
err = cfgNetState.Read(id) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = gOper.Read(d.stateDriver, cfgNetState.Tenant) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
operNwState = OvsOperNetworkState{StateDriver: d.stateDriver, | ||
Id: cfgNetState.Id, Tenant: cfgNetState.Tenant, | ||
PktTagType: cfgNetState.PktTagType, | ||
DefaultGw: cfgNetState.DefaultGw} | ||
|
||
if cfgNetState.PktTag == "auto" { | ||
operNwState.PktTagType = gOper.DefaultNetType | ||
if gOper.DefaultNetType == "vlan" { | ||
pktTag, err = gOper.AllocVlan() | ||
if err != nil { | ||
return err | ||
} | ||
} else if gOper.DefaultNetType == "vxlan" { | ||
extPktTag, pktTag, err = gOper.AllocVxlan() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
err = gOper.Update(d.stateDriver) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("allocated vlan %d vxlan %d \n", pktTag, extPktTag) | ||
operNwState.ExtPktTag = int(extPktTag) | ||
operNwState.PktTag = int(pktTag) | ||
} else { | ||
if cfgNetState.PktTagType == "vxlan" { | ||
pktTag, err = gOper.AllocLocalVlan() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = gOper.Update(d.stateDriver) | ||
if err != nil { | ||
log.Printf("error updating the global state - %s \n", err) | ||
return err | ||
} | ||
|
||
operNwState.PktTag = int(pktTag) | ||
operNwState.ExtPktTag, err = strconv.Atoi(cfgNetState.PktTag) | ||
} else if cfgNetState.PktTagType == "vlan" { | ||
operNwState.PktTag, err = strconv.Atoi(cfgNetState.PktTag) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
if cfgNetState.SubnetIp == "auto" { | ||
subnetLen = gOper.AllocSubnetLen | ||
subnetIp, err = gOper.AllocSubnet() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = gOper.Update(d.stateDriver) | ||
if err != nil { | ||
return err | ||
} | ||
log.Printf("allocated subnet %s/%d \n", subnetIp, subnetLen) | ||
} else { | ||
subnetLen = cfgNetState.SubnetLen | ||
subnetIp = cfgNetState.SubnetIp | ||
} | ||
operNwState.SubnetIp = subnetIp | ||
operNwState.SubnetLen = subnetLen | ||
|
||
netutils.InitSubnetBitset(&operNwState.IpAllocMap, subnetLen) | ||
err = operNwState.Write() | ||
err := cfgNetState.Read(id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need a read operation here, since the CreateNetwork() is a noop? I am thinking why add extra error paths if we can live without them. I also see that we have stopped creating OvsNwOperState in this function altogether. It was mostly used to track number of endpoints in a network. It might be a useful stats to keep and maintain for debugging from netplugin perspective, albeit it is much coarser stats and needs multiple distributed instance of the netplugin to synchronize updates to OvsNwOperState (I was able to achieve the synchronization in my other branch 'deleteApiCleanup' without an explicit lock). But not sure if we loose much by removing this state. I think we can start off with current change as it keeps things simple. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason for read operation was to suggest that we can continue to perform any OVS operations based on that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand NwOperState is cluster wide and EpCount was cluster wide (we can achieve it's update with serialized writes as I mentioned in my comment). I was just mentioning it could be updated by Driver for debuggabilty. Example, it could be used to indicate some failure when master thinks they have placed X EPs in a network but the Driver state would indicate say (X'). I agree it's a coarser stats though and won't narrow down to failing minion but still indicates some failure. Also, looks like we will be getting rid of OvsNwOperState for now based on rest of the comments so not a concern right now 😉 |
||
if err != nil { | ||
log.Printf("Failed to read net %s \n", cfgNetState.Id) | ||
return err | ||
} | ||
log.Printf("create net %s \n", cfgNetState.Id) | ||
|
||
return nil | ||
} | ||
|
||
func (d *OvsDriver) DeleteNetwork(value string) error { | ||
|
||
// no driver operation for network delete | ||
var err error | ||
var gOper gstate.Oper | ||
|
||
cfgNetState := OvsCfgNetworkState{} | ||
err = cfgNetState.Unmarshal(value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest to remove the dependence on the 'value' being passed in this function since it is going to be a noop. This will take us closer to cleaning up the Delete API to revert them to take keys instead, which I am trying to get to in my deleteApiCleanup branch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't changed this code of passing the value; rather my change is removing gOper reference from it. I'd expect this to be cleaned up as part of your changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, will do. |
||
if err != nil { | ||
log.Printf("Failed to unmarshal network config, err '%s' \n", err) | ||
return err | ||
} | ||
|
||
operNwState := OvsOperNetworkState{StateDriver: d.stateDriver} | ||
err = operNwState.Read(cfgNetState.Id) | ||
if err != nil { | ||
return core.ErrIfKeyExists(err) | ||
} | ||
|
||
err = gOper.Read(d.stateDriver, operNwState.Tenant) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if operNwState.PktTagType == "vlan" { | ||
err = gOper.FreeVlan(uint(operNwState.PktTag)) | ||
if err != nil { | ||
return err | ||
} | ||
} else if operNwState.PktTagType == "vxlan" { | ||
err = gOper.FreeVxlan(uint(operNwState.ExtPktTag), | ||
uint(operNwState.PktTag)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if cfgNetState.SubnetIp == "auto" { | ||
log.Printf("freeing subnet %s/%d \n", | ||
operNwState.SubnetIp, operNwState.SubnetLen) | ||
err = gOper.FreeSubnet(operNwState.SubnetIp) | ||
if err != nil { | ||
log.Printf("error '%s' freeing the subnet \n", err) | ||
} | ||
} | ||
|
||
err = gOper.Update(d.stateDriver) | ||
if err != nil { | ||
log.Printf("error updating the global state - %s \n", err) | ||
return err | ||
} | ||
|
||
err = operNwState.Clear() | ||
if err != nil { | ||
return core.ErrIfKeyExists(err) | ||
} | ||
log.Printf("delete net %s \n", cfgNetState.Id) | ||
|
||
return nil | ||
} | ||
|
||
func (d *OvsDriver) CreateEndpoint(id string) error { | ||
var err error | ||
var ipAddrBit uint = 0 | ||
var found bool | ||
|
||
// add an internal ovs port with vlan-tag information from the state | ||
portName := d.getPortName() | ||
|
@@ -621,14 +485,6 @@ func (d *OvsDriver) CreateEndpoint(id string) error { | |
intfType = "" | ||
} | ||
|
||
cfgNetState := OvsCfgNetworkState{StateDriver: d.stateDriver} | ||
err = cfgNetState.Read(epCfg.NetId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// TODO: use etcd distributed lock to ensure atomicity of this operation | ||
// this is valid for EpCount, defer the unlocking for intermediate returns | ||
operNwState := OvsOperNetworkState{StateDriver: d.stateDriver} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not be using OvsOperNetworkState but rather OvsConfigNetworkState, since oper state is owned and created by the (ovs-)driver (if it needs to), while the config state is owned by the management function and is just consumed by the driver. Just to clarify, I am talking something different wrt intent v/s realized-intent. I see each module/interface (driver; resource allocator etc) to own their own config(consumed) and oper(generated) states. One module's oper state may become other module's config state (assuming no need for state translation). This helps keep state boundaries clear and provide flexibility of reuse of module's functionality. Example 1, netmaster can consume MasterNwConfig and generate OvsNwConfig or ACINwConfig based on state translation function. Example 2, I will be able to mix IP-allocator with ovs as well as Linux-bridge driver, if I define IP-allocator's own config and oper state and then translate it's oper state to config state of underlying driver). This is inline with what we put in the design document around intermixing independent functionalities through state and state translation. |
||
err = operNwState.Read(epCfg.NetId) | ||
if err != nil { | ||
|
@@ -647,52 +503,13 @@ func (d *OvsDriver) CreateEndpoint(id string) error { | |
} | ||
}() | ||
|
||
ipAddress := epCfg.IpAddress | ||
if ipAddress == "auto" { | ||
if ipAddrBit, found = operNwState.IpAllocMap.NextClear(0); !found { | ||
log.Printf("auto allocation failed - address exhaustion in subnet %s/%d \n", | ||
operNwState.SubnetIp, operNwState.SubnetLen) | ||
return err | ||
} | ||
ipAddress, err = netutils.GetSubnetIp( | ||
operNwState.SubnetIp, operNwState.SubnetLen, 32, ipAddrBit) | ||
if err != nil { | ||
log.Printf("error acquiring subnet ip '%s' \n", err) | ||
return err | ||
} | ||
log.Printf("Ep %s was allocated ip address %s \n", id, ipAddress) | ||
} else if ipAddress != "" && operNwState.SubnetIp != "" { | ||
ipAddrBit, err = netutils.GetIpNumber( | ||
operNwState.SubnetIp, operNwState.SubnetLen, 32, ipAddress) | ||
if err != nil { | ||
log.Printf("error getting host id from hostIp %s Subnet %s/%d err '%s'\n", | ||
ipAddress, operNwState.SubnetIp, operNwState.SubnetLen, err) | ||
return err | ||
} | ||
} | ||
operNwState.IpAllocMap.Set(ipAddrBit) | ||
|
||
// deprecate - bitset.WordCount gives the following value | ||
operNwState.EpCount += 1 | ||
err = operNwState.Write() | ||
if err != nil { | ||
return err | ||
} | ||
defer func() { | ||
if err != nil { | ||
//XXX: need to return allocated ip back to the pool in case of failure | ||
operNwState.EpCount -= 1 | ||
operNwState.Write() | ||
} | ||
}() | ||
|
||
operEpState := OvsOperEndpointState{ | ||
StateDriver: d.stateDriver, | ||
Id: id, | ||
PortName: portName, | ||
NetId: epCfg.NetId, | ||
ContName: epCfg.ContName, | ||
IpAddress: ipAddress, | ||
IpAddress: epCfg.IpAddress, | ||
IntfName: intfName, | ||
HomingHost: epCfg.HomingHost, | ||
VtepIp: epCfg.VtepIp} | ||
|
@@ -752,27 +569,6 @@ func (d *OvsDriver) DeleteEndpoint(value string) (err error) { | |
return err | ||
} | ||
|
||
operNwState := OvsOperNetworkState{StateDriver: d.stateDriver} | ||
err = operNwState.Read(epOper.NetId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ipAddrBit, err := netutils.GetIpNumber( | ||
operNwState.SubnetIp, operNwState.SubnetLen, 32, epOper.IpAddress) | ||
if err != nil { | ||
log.Printf("error getting host id from %s subnet %s/%d err '%s'\n", | ||
epOper.IpAddress, operNwState.SubnetIp, operNwState.SubnetLen, err) | ||
} else { | ||
operNwState.IpAllocMap.Clear(ipAddrBit) | ||
} | ||
|
||
operNwState.EpCount -= 1 | ||
err = operNwState.Write() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -262,12 +262,12 @@ func (g *Oper) AllocVxlan() (vxlan uint, localVlan uint, err error) { | |
|
||
func (g *Oper) FreeVxlan(vxlan uint, localVlan uint) error { | ||
if !g.FreeLocalVlans.Test(localVlan) { | ||
g.FreeLocalVlans.Clear(localVlan) | ||
g.FreeLocalVlans.Set(localVlan) | ||
} | ||
|
||
vxlan = vxlan - g.FreeVxlansStart | ||
if !g.FreeVxlans.Test(vxlan) { | ||
g.FreeVxlans.Clear(vxlan) | ||
g.FreeVxlans.Set(vxlan) | ||
} | ||
|
||
return nil | ||
|
@@ -279,15 +279,15 @@ func (g *Oper) AllocLocalVlan() (uint, error) { | |
return 0, errors.New("no vlans available ") | ||
} | ||
|
||
g.FreeLocalVlans.Set(vlan) | ||
g.FreeLocalVlans.Clear(vlan) | ||
|
||
return vlan, nil | ||
} | ||
|
||
// be idempotent, don't complain if vlan is already freed | ||
func (g *Oper) FreeLocalVlan(vlan uint) error { | ||
if !g.FreeLocalVlans.Test(vlan) { | ||
g.FreeLocalVlans.Clear(vlan) | ||
g.FreeLocalVlans.Set(vlan) | ||
} | ||
return nil | ||
} | ||
|
@@ -333,11 +333,28 @@ func (g *Oper) AllocVlan() (uint, error) { | |
// be idempotent, don't complain if vlan is already freed | ||
func (g *Oper) FreeVlan(vlan uint) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a minor nit. I was hoping we could remodel gstate into a resource allocator interface, so that we don't need to have explicit APIs like Set/FreeVlan, Set/FreeVxlan etc. A resource allocator interface can potentially look something like this:
I just wanted to point this out so that we are on same page wrt resource-allocation design but don't need to change it now. Can safely be addressed later in case we start identifying more resources to deal with. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for putting out the resource allocation abstraction API; Will do it as a separate change, but a generic nature of interface makes a lot of sense to me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, thanks Vipin. |
||
if !g.FreeVlans.Test(vlan) { | ||
g.FreeVlans.Clear(vlan) | ||
g.FreeVlans.Set(vlan) | ||
} | ||
return nil | ||
} | ||
|
||
func (g *Oper) CheckVlanInUse(vlan uint) error { | ||
if !g.FreeVlans.Test(vlan) { | ||
return errors.New("specified vlan not available") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (g *Oper) SetVlan(vlan uint) (err error) { | ||
err = g.CheckVlanInUse(vlan) | ||
if err == nil { | ||
g.FreeVlans.Clear(vlan) | ||
} | ||
|
||
return | ||
} | ||
|
||
func (g *Oper) AllocSubnet() (string, error) { | ||
|
||
subnetId, found := g.FreeSubnets.NextSet(0) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggest to remove the commented code instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will do.