Skip to content
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

phantom ip/mac vlan network after a powercycle #2295

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 41 additions & 24 deletions drivers/ipvlan/ipvlan_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,14 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
// empty parent and --internal are handled the same. Set here to update k/v
config.Internal = true
}
err = d.createNetwork(config)
foundExisting, err := d.createNetwork(config)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the re-create path, can you please add some integration test-cases, either in libnetwork or moby (https://github.com/moby/moby/blob/master/integration/network/service_test.go) . A SIGKILL on dockerd should be enough to reproduce this case

if err != nil {
return err
}

if foundExisting {
return nil
}
// update persistent db, rollback on fail
err = d.storeUpdate(config)
if err != nil {
Expand All @@ -76,22 +80,31 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
}

// createNetwork is used by new network callbacks and persistent network cache
func (d *driver) createNetwork(config *configuration) error {
func (d *driver) createNetwork(config *configuration) (bool, error) {
foundExisting := false
networkList := d.getNetworks()
for _, nw := range networkList {
if config.Parent == nw.config.Parent {
return fmt.Errorf("network %s is already using parent interface %s",
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
if config.ID != nw.config.ID {
return false, fmt.Errorf("network %s is already using parent interface %s",
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
}
logrus.Debugf("Create Network for the same ID %s\n", config.ID)
foundExisting = true
break
}
}
if !parentExists(config.Parent) {
// if the --internal flag is set, create a dummy link
if config.Internal {
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
if err != nil {
return err
}
config.CreatedSlaveLink = true
if !dummyLinkExists(getDummyName(stringid.TruncateID(config.ID))) {
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
if err != nil {
return false, err
}
config.CreatedSlaveLink = true
}

// notify the user in logs they have limited communications
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
Expand All @@ -100,24 +113,28 @@ func (d *driver) createNetwork(config *configuration) error {
} else {
// if the subinterface parent_iface.vlan_id checks do not pass, return err.
// a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
err := createVlanLink(config.Parent)
if err != nil {
return err
}
// if driver created the networks slave link, record it for future deletion
config.CreatedSlaveLink = true
if !vlanLinkExists(config.Parent) {
err := createVlanLink(config.Parent)
if err != nil {
return false, err
}
// if driver created the networks slave link, record it for future deletion
config.CreatedSlaveLink = true
}
}
}
n := &network{
id: config.ID,
driver: d,
endpoints: endpointTable{},
config: config,
}
// add the *network
d.addNetwork(n)
if !foundExisting {
n := &network{
id: config.ID,
driver: d,
endpoints: endpointTable{},
config: config,
}
// add the *network
d.addNetwork(n)
}

return nil
return foundExisting, nil
}

// DeleteNetwork the network for the specified driver type
Expand Down
17 changes: 17 additions & 0 deletions drivers/ipvlan/ipvlan_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ func parentExists(ifaceStr string) bool {
return true
}

func vlanLinkExists(linkStr string) bool {
_, err := ns.NlHandle().LinkByName(linkStr)
if err != nil {
return false
}
return true
}

// createVlanLink parses sub-interfaces and vlan id for creation
func createVlanLink(parentName string) error {
if strings.Contains(parentName, ".") {
Expand Down Expand Up @@ -156,6 +164,15 @@ func parseVlan(linkName string) (string, int, error) {
return parent, vidInt, nil
}

func dummyLinkExists(dummyName string) bool {
_, err := ns.NlHandle().LinkByName(dummyName)
if err != nil {
return false
}
return true
}


// createDummyLink creates a dummy0 parent link
func createDummyLink(dummyName, truncNetID string) error {
// create a parent interface since one was not specified
Expand Down
11 changes: 9 additions & 2 deletions drivers/ipvlan/ipvlan_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ func (d *driver) initStore(option map[string]interface{}) error {
return types.InternalErrorf("ipvlan driver failed to initialize data store: %v", err)
}

return d.populateNetworks()
err = d.populateNetworks()
if err != nil {
return err
}
err = d.populateEndpoints()
if err != nil {
return err
}
}

return nil
Expand All @@ -73,7 +80,7 @@ func (d *driver) populateNetworks() error {
}
for _, kvo := range kvol {
config := kvo.(*configuration)
if err = d.createNetwork(config); err != nil {
if _, err = d.createNetwork(config); err != nil {
logrus.Warnf("could not create ipvlan network for id %s from persistent state", config.ID)
}
}
Expand Down
70 changes: 47 additions & 23 deletions drivers/macvlan/macvlan_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,15 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
// empty parent and --internal are handled the same. Set here to update k/v
config.Internal = true
}
err = d.createNetwork(config)
foundExisting, err := d.createNetwork(config)
if err != nil {
return err
}

if foundExisting {
return nil
}

// update persistent db, rollback on fail
err = d.storeUpdate(config)
if err != nil {
Expand All @@ -80,22 +85,32 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
}

// createNetwork is used by new network callbacks and persistent network cache
func (d *driver) createNetwork(config *configuration) error {
func (d *driver) createNetwork(config *configuration) (bool, error) {
foundExisting := false
networkList := d.getNetworks()
for _, nw := range networkList {
if config.Parent == nw.config.Parent {
return fmt.Errorf("network %s is already using parent interface %s",
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
if config.ID != nw.config.ID {
return false, fmt.Errorf("network %s is already using parent interface %s",
getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
}
logrus.Debugf("Create Network for the same ID %s\n", config.ID)
foundExisting = true
break
}
}
if !parentExists(config.Parent) {
// if the --internal flag is set, create a dummy link
if config.Internal {
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
if err != nil {
return err
if !dummyLinkExists(getDummyName(stringid.TruncateID(config.ID))) {
err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
if err != nil {
return false, err
}
config.CreatedSlaveLink = true
} else {
logrus.Debugf("Dummy Link %s for Mac Vlan already exists", getDummyName(stringid.TruncateID(config.ID)))
}
config.CreatedSlaveLink = true
// notify the user in logs they have limited communications
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
Expand All @@ -104,24 +119,33 @@ func (d *driver) createNetwork(config *configuration) error {
} else {
// if the subinterface parent_iface.vlan_id checks do not pass, return err.
// a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
err := createVlanLink(config.Parent)
if err != nil {
return err
}
// if driver created the networks slave link, record it for future deletion
config.CreatedSlaveLink = true

if !vlanLinkExists(config.Parent) {
// if the subinterface parent_iface.vlan_id checks do not pass, return err.
// a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
err := createVlanLink(config.Parent)
if err != nil {
return false, err
}
// if driver created the networks slave link, record it for future deletion
config.CreatedSlaveLink = true
} else {
logrus.Debugf("Parent Sub Interface %s already Exists NetID %s", config.Parent, config.ID)
}
}
}
n := &network{
id: config.ID,
driver: d,
endpoints: endpointTable{},
config: config,
}
// add the *network
d.addNetwork(n)
if !foundExisting {
n := &network{
id: config.ID,
driver: d,
endpoints: endpointTable{},
config: config,
}
// add the *network
d.addNetwork(n)
}

return nil
return foundExisting, nil
}

// DeleteNetwork deletes the network for the specified driver type
Expand Down
17 changes: 17 additions & 0 deletions drivers/macvlan/macvlan_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ func parentExists(ifaceStr string) bool {
return true
}

func vlanLinkExists(linkStr string) bool {
_, err := ns.NlHandle().LinkByName(linkStr)
if err != nil {
return false
}
return true
}

// createVlanLink parses sub-interfaces and vlan id for creation
func createVlanLink(parentName string) error {
if strings.Contains(parentName, ".") {
Expand Down Expand Up @@ -160,6 +168,15 @@ func parseVlan(linkName string) (string, int, error) {
return parent, vidInt, nil
}

func dummyLinkExists(dummyName string) bool {
_, err := ns.NlHandle().LinkByName(dummyName)
if err != nil {
return false
}
return true
}


// createDummyLink creates a dummy0 parent link
func createDummyLink(dummyName, truncNetID string) error {
// create a parent interface since one was not specified
Expand Down
12 changes: 10 additions & 2 deletions drivers/macvlan/macvlan_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ func (d *driver) initStore(option map[string]interface{}) error {
return types.InternalErrorf("macvlan driver failed to initialize data store: %v", err)
}

return d.populateNetworks()
err = d.populateNetworks()
if err != nil {
return err
}
err = d.populateEndpoints()
if err != nil {
return err
}

}

return nil
Expand All @@ -73,7 +81,7 @@ func (d *driver) populateNetworks() error {
}
for _, kvo := range kvol {
config := kvo.(*configuration)
if err = d.createNetwork(config); err != nil {
if _, err = d.createNetwork(config); err != nil {
logrus.Warnf("Could not create macvlan network for id %s from persistent state", config.ID)
}
}
Expand Down
5 changes: 5 additions & 0 deletions endpoint_cnt.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,8 @@ func (ec *endpointCnt) IncEndpointCnt() error {
func (ec *endpointCnt) DecEndpointCnt() error {
return ec.atomicIncDecEpCnt(false)
}

func (ec *endpointCnt) SetEndpointCnt(cnt uint64) error {
return ec.setCnt(cnt)
}

8 changes: 7 additions & 1 deletion network.go
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,13 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error {

if n.ConfigFrom() != "" {
if t, err := c.getConfigNetwork(n.ConfigFrom()); err == nil {
if err := t.getEpCnt().DecEndpointCnt(); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for bridge networks , multiple networks can be created from a config (--config-from), so this will break that case

Copy link
Contributor

@arkodg arkodg May 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// ConfigOnly and ConfigFrom Networks have 1 to 1 correspondence? In that case
// there is no need to decrement the ref count, simply set it to zero.
// In the case of a powercycle, the count on the configfrom network increments more than
// once and this can cause the configfrom network to linger forever and not even deleteable.
// This happens due to the additional increment after the power on, hence count can never
// become zero.
if err := t.getEpCnt().SetEndpointCnt(0); err != nil {
logrus.Warnf("Failed to update reference count for configuration network %q on removal of network %q: %v",
t.Name(), n.Name(), err)
}
Expand Down