Skip to content

Commit

Permalink
BUGFIX: Major overhaul of the Dell x1052 ethernet switch configuratio…
Browse files Browse the repository at this point in the history
…n code.

BREAKING CHANGE: Apply 'ALTER TABLE switchports DROP PRIMARY KEY;' to database.
  • Loading branch information
gregorybruno authored and Ryan Santos committed Jul 16, 2018
1 parent 9c4adff commit c50f98b
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 260 deletions.
3 changes: 1 addition & 2 deletions common/nodes/database-schema.xml
Expand Up @@ -189,8 +189,7 @@ DROP TABLE IF EXISTS switchports;
CREATE TABLE switchports (
interface int(11) NOT NULL references networks,
switch int(11) NOT NULL references nodes,
port int(11) NOT NULL,
PRIMARY KEY(switch, port)
port int(11) NOT NULL
);

<!-- Routes -->
Expand Down
132 changes: 52 additions & 80 deletions common/src/stack/command/stack/commands/__init__.py
Expand Up @@ -280,91 +280,53 @@ def getSwitchNetwork(self, switch):

return network

def addSwitchHost(self, switch, host, port, interface):
"""
Add a host to switch.
Check if host has an interface on the same network as
the switch
"""
def doSwitchHost(self, switch, port, host, interface, action):
rows = self.db.select(""" id from networks
where node=(select id from nodes where name='%s')
and device='%s' """ % (host, interface))

# Get the switch's network
switch_network = self.db.select("""
subnet from networks where node=(
select id from nodes where name='%s'
)
""" % switch)
try:
host_interface = rows[0][0]
except:
host_interface = None

if not switch_network:
if not host_interface:
raise CommandError(self,
"switch '%s' doesn't have an interface" % switch)

# Get the interface of the host that is on the same
# network as the switch

# If the user entered an interface
if interface:
host_interface = self.db.select("""
id from networks
where subnet='%s'
and node=(select id from nodes where name='%s')
and device='%s'
""" % (switch_network[0][0], host, interface))

if not host_interface:
raise CommandError(self,
"Interface '%s' isn't on a network with '%s'"
% ( interface, switch ))

# Grab the interface, if there is one, that is on the same network
# as the switch
else:
host_interface = self.db.select("""
id from networks where subnet='%s' and
node=(select id from nodes where name='%s')
""" % (switch_network[0][0], host))

if not host_interface:
raise CommandError(self,
"host '%s' is not on a network with switch '%s'"
% ( host, switch ))
"Interface '%s' isn't defined for host '%s'"
% (interface, switch))

# Check if the port is already managed by the switch
rows = self.db.select("""
* from switchports
where port='%s'
and switch=(select id from nodes where name='%s')
""" % (port, switch))
rows = self.db.select(""" * from switchports
where interface='%s' and port='%s'
and switch=(select id from nodes where name='%s')"""
% (host_interface, port, switch))

if action == 'remove' and rows:
# delete it
self.db.execute(""" delete from switchports
where interface='%s'
and switch=(select id from nodes where name = '%s')
and port='%s' """ % (host_interface, switch, port))

elif action == 'add' and not rows:
# add it
self.db.execute(""" insert into switchports
(interface, switch, port)
values ('%s', (select id from nodes where name = '%s'),
'%s') """ % (host_interface, switch, port))

def addSwitchHost(self, switch, port, host, interface):
"""
Add a host/interface to a switch/port
"""
self.doSwitchHost(switch, port, host, interface, 'add')

def delSwitchHost(self, switch, port, host, interface):
"""
Remove a host/interface from a switch/port
"""
self.doSwitchHost(switch, port, host, interface, 'remove')

if rows:
raise CommandError(self,
"Switch '%s' is alredy managing a host on port '%s'"
% (switch, port))

# if we got here, add the host to be managed switch
query = """
insert into switchports
(interface, switch, port)
values ('%s',
(select id from nodes where name = '%s'),
'%s')
""" % (host_interface[0][0], switch, port)

self.db.execute(' '.join(query.split()))

def delSwitchHost(self, switch, host):
"""Add a host to switch"""
query = """
delete from switchports
where interface in (
select id from networks where
node=(select id from nodes where name='%s') and
subnet=(select subnet from networks where
node=(select id from nodes where name='%s'))
)
and switch=(select id from nodes where name='%s')
""" % (host, switch, switch)
#print(query)
self.db.execute(' '.join(query.split()))
def setSwitchHostVlan(self, switch, host, vlan):
self.db.execute("""
update switchports
Expand Down Expand Up @@ -1367,7 +1329,17 @@ def getHostRoutes(self, host, showsource=0):
routes = {}

# if needed, add default routes to support multitenancy
if _frontend == host:
# if _frontend == host:
if 0:
print(
"""
n.ip, n.device, np.ip
from networks n
left join networks np
on np.node != (select id from nodes where name='%s') and n.subnet = np.subnet
where n.node=(select id from nodes where name='%s')
""" % (_frontend, _frontend))

_networks = self.select(
"""
n.ip, n.device, np.ip
Expand Down
Expand Up @@ -34,7 +34,7 @@ def run(self, args):
netmask = o['mask']
gateway = o['gateway']

startmode = 'off'
startmode = None
bootproto = 'static'

if netname and ip and netmask:
Expand Down Expand Up @@ -103,6 +103,7 @@ def run(self, args):
parent_device = interface.strip().split('.')[0]
self.owner.addOutput(host, 'ETHERDEVICE=%s' % parent_device)
self.owner.addOutput(host, 'VLAN=yes')
startmode = 'auto'
else:
self.owner.addOutput(host, 'USERCONTROL=no')

Expand All @@ -119,18 +120,15 @@ def run(self, args):

if 'onboot=no' in options:
startmode = 'manual'
else:
if ip or dhcp or channel or 'bridge' in options:
#
# if there is an IP address, or this
# interface should DHCP, or anything in
# the 'channel' field (e.g., this is a
# bridged or bonded interface), or if 'bridge'
# is in the options, then turn this interface on
#
startmode = 'auto'
else:
startmode = 'off'
elif ip or dhcp or channel or 'bridge' in options:
#
# if there is an IP address, or this
# interface should DHCP, or anything in
# the 'channel' field (e.g., this is a
# bridged or bonded interface), or if 'bridge'
# is in the options, then turn this interface on
#
startmode = 'auto'

if not dhcp:
if ip:
Expand Down Expand Up @@ -186,6 +184,9 @@ def run(self, args):
startmode = 'auto'
bootproto = 'none'

if not startmode:
startmode = 'off'

self.owner.addOutput(host, 'STARTMODE=%s' % startmode)
self.owner.addOutput(host, 'BOOTPROTO=%s' % bootproto)

Expand Down
5 changes: 3 additions & 2 deletions common/src/stack/switch/command/add/switch/host/__init__.py
Expand Up @@ -56,7 +56,8 @@ def run(self, params, args):
for switch in switches:
# Make sure switch has an interface
if self.getSwitchNetwork(switch):
self.addSwitchHost(switch, host, port, interface)
self.addSwitchHost(switch, port, host, interface)
else:
raise CommandError(self,
"switch '%s' doesn't have an interface" % switch)
"switch '%s' doesn't have a management interface" % switch)

@@ -0,0 +1,35 @@
# @copyright@
# Copyright (c) 2006 - 2017 Teradata
# All rights reserved. Stacki(r) v5.x stacki.com
# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
# @copyright@

import stack.commands
from stack.exception import ArgRequired

class command(stack.commands.HostArgumentProcessor, stack.commands.create.command):
pass

class Command(command):
"""
This command dynamically maps the interfaces of hosts to ports of a switch.
<arg name="host">
The hosts to map. If no hosts are supplied, map all hosts.
</arg>
<param type='string' name='switch'>
The switch to map. If no switches are supplied, then map all switches.
</param>
"""

def run(self, params, args):
switch, = self.fillParams([ ('switch', None) ])

switches = self.getHostnames(switch)
hosts = self.getHostnames(args)

for s in switches:
model = self.getHostAttr(s, 'component.model')
self.runImplementation(model, (s, hosts))

@@ -0,0 +1,28 @@
#
# @copyright@
# Copyright (c) 2006 - 2018 Teradata
# All rights reserved. Stacki(r) v5.x stacki.com
# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
# @copyright@
#

import stack.commands

class Implementation(stack.commands.Implementation):
def run(self, args):
(switch, hosts) = args

list_switch_mac = self.owner.call('list.switch.mac', [ switch ])

for h in self.owner.call('list.host.interface', hosts):
if h['interface'] == 'ipmi':
continue

for m in list_switch_mac:
if h['host'] == m['host'] and h['interface'] == m['interface']:
params = [ switch, 'host=%s' % h['host'],
'interface=%s' % m['interface'],
'port=%s' % m['port'] ]
self.owner.call('add.switch.host', params)
break

10 changes: 6 additions & 4 deletions common/src/stack/switch/command/list/switch/config/imp_x1052.py
Expand Up @@ -24,17 +24,18 @@ def run(self, args):
frontend_tftp_address = _frontend['ip']
switch_address = switch['ip']
switch_name = switch['host']
switch_username = self.owner.getHostAttr(switch_name, 'switch_username')
switch_password = self.owner.getHostAttr(switch_name, 'switch_password')
switch_username = self.owner.getHostAttr(switch_name, 'switch_username')
switch_password = self.owner.getHostAttr(switch_name, 'switch_password')

# Connect to the switch
with SwitchDellX1052(switch_address, switch_name, switch_username, switch_password) as switch:
try:
switch.set_tftp_ip(frontend_tftp_address)
switch.connect()
switch.download()

with open('/tftpboot/pxelinux/%s_running_config' % switch_name, 'r') as f:

filename = '%s/%s' % (switch.tftpdir, switch.current_config)
with open(filename, 'r') as f:
lines = f.readlines()
_printline = True
_block = {}
Expand Down Expand Up @@ -71,3 +72,4 @@ def run(self, args):
raise CommandError(self, switch_error)
except Exception:
raise CommandError(self, "There was an error downloading the running config from the switch")

24 changes: 19 additions & 5 deletions common/src/stack/switch/command/remove/switch/host/__init__.py
Expand Up @@ -8,6 +8,7 @@
from stack.exception import CommandError, ArgRequired

class command(stack.commands.SwitchArgumentProcessor,
stack.commands.HostArgumentProcessor,
stack.commands.remove.command):
pass

Expand All @@ -19,21 +20,34 @@ class Command(command):
The switch you are going to remove the host from.
</arg>
<param optional='0' type='string' name='host'>
The name of the host you want to stop managing.
<param type='string' name='host' optional='0'>
Name of the host (e.g., backend-0-0).
</param>
<param type='string' name='port' optional='0'>
Port on the switch the host is connected to (e.g., 4).
</param>
<param type='string' name='interface' optional='0'>
Name of the host's interface that is connected to the switch (e.g., 'eth0').
</param>
"""

def run(self, params, args):

if len(args) < 1:
raise ArgRequired(self, 'switch')

switch, = self.getSwitchNames(args)

host, = self.fillParams([
host, port, interface = self.fillParams([
('host', None, True),
('port', None, True),
('interface', None, True)
])

self.delSwitchHost(switch, host)
# Check if host exists
hosts = self.getHostnames([host])

self.delSwitchHost(switch, port, host, interface)

8 changes: 4 additions & 4 deletions common/src/stack/switch/command/report/switch/__init__.py
Expand Up @@ -15,10 +15,10 @@ class command(stack.commands.HostArgumentProcessor,

class Command(command):
"""
Output the switch configuration file to tftp directory.
Output the switch configuration file.
<example cmd='report switch'>
Outputs data for /tftpboot/pxelunux/upload
<example cmd='report switch ethernet-1-1'>
Output the configation file for ethernet-1-1.
</example>
"""

Expand All @@ -30,6 +30,6 @@ def run(self, params, args):

switch_name = switch['switch']
model = self.getHostAttr(switch_name, 'component.model')
self.runImplementation(model, [switch])
self.runImplementation(model, [ switch ])

self.endOutput(padChar='', trimOwner=True)

0 comments on commit c50f98b

Please sign in to comment.