Commit ef4551b8 authored by Alain Takoudjou's avatar Alain Takoudjou

Merge branch 'tap-nobridge' to master

Conflicts:
	master/bt5/slapos_pdm/SkinTemplateItem/portal_skins/slapos_pdm/SoftwareProduct_getSortedSoftwareReleaseList.xml
parents 28753466 eb76d6cc
...@@ -65,6 +65,10 @@ ...@@ -65,6 +65,10 @@
if current_dict in to_add_ip_dict_list:\n if current_dict in to_add_ip_dict_list:\n
to_add_ip_dict_list.remove(current_dict)\n to_add_ip_dict_list.remove(current_dict)\n
else:\n else:\n
# XXX - Only delete if Network interface are supposed to be the same\n
if additional_dict.has_key(\'network_interface\'):\n
if address.getNetworkInterface(\'\') and additional_dict[\'network_interface\'] != address.getNetworkInterface():\n
continue\n
to_delete_ip_id_list.append(address.getId())\n to_delete_ip_id_list.append(address.getId())\n
\n \n
for address in to_add_ip_dict_list:\n for address in to_add_ip_dict_list:\n
...@@ -114,6 +118,16 @@ for send_partition in computer_dict[\'partition_list\']:\n ...@@ -114,6 +118,16 @@ for send_partition in computer_dict[\'partition_list\']:\n
partition.edit(reference=send_partition[\'reference\'])\n partition.edit(reference=send_partition[\'reference\'])\n
network_interface = send_partition[\'tap\'][\'name\']\n network_interface = send_partition[\'tap\'][\'name\']\n
compareAndUpdateAddressList(partition, send_partition[\'address_list\'], {\'network_interface\': network_interface})\n compareAndUpdateAddressList(partition, send_partition[\'address_list\'], {\'network_interface\': network_interface})\n
tap_addr_list = []\n
additional_dict = {\'network_interface\': \'route_\' + network_interface}\n
if send_partition[\'tap\'].has_key(\'ipv4_addr\') and send_partition[\'tap\'][\'ipv4_addr\']:\n
tap_addr_list.append({\n
\'addr\': send_partition[\'tap\'][\'ipv4_addr\'],\n
\'netmask\': send_partition[\'tap\'][\'ipv4_netmask\']\n
})\n
additional_dict[\'gateway_ip_address\'] = send_partition[\'tap\'][\'ipv4_gateway\']\n
additional_dict[\'network_address\'] = send_partition[\'tap\'][\'ipv4_network\']\n
compareAndUpdateAddressList(partition, tap_addr_list, additional_dict)\n
\n \n
# Desactivate all other partitions\n # Desactivate all other partitions\n
for key, value in existing_partition_dict.items():\n for key, value in existing_partition_dict.items():\n
......
...@@ -131,6 +131,51 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin): ...@@ -131,6 +131,51 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin):
self.assertEqual(address.getNetworkInterface(), 'bar') self.assertEqual(address.getNetworkInterface(), 'bar')
self.assertEqual(address.getId(), 'default_network_address') self.assertEqual(address.getId(), 'default_network_address')
def test_CreateSinglePartition_TapNetworkInformation(self):
partition = self.computer.newContent(
reference='foo',
portal_type='Computer Partition',
)
# No address in the empty partition
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 0)
parameter_dict = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
}],
'tap': {'name': 'bar',
'ipv4_addr': 'e',
'ipv4_netmask': 'f',
'ipv4_network': 'g',
'ipv4_gateway': 'h'},
}],
'address': 'a',
'netmask': 'b',
}
self.computer.Computer_updateFromDict(parameter_dict)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 2)
address_list.sort(key=lambda x: {0: 1, 1: 2}[int(x.getId() != 'default_network_address')])
address = address_list[0]
self.assertEqual(address.getIpAddress(), 'c')
self.assertEqual(address.getNetmask(), 'd')
self.assertEqual(address.getNetworkInterface(), 'bar')
self.assertEqual(address.getId(), 'default_network_address')
address1 = address_list[1]
self.assertEqual(address1.getIpAddress(), 'e')
self.assertEqual(address1.getNetmask(), 'f')
self.assertEqual(address1.getNetworkAddress(), 'g')
self.assertEqual(address1.getGatewayIpAddress(), 'h')
self.assertEqual(address1.getNetworkInterface(), 'route_bar')
def test_CreateMultiplePartitionNetworkInformation(self): def test_CreateMultiplePartitionNetworkInformation(self):
partition = self.computer.newContent( partition = self.computer.newContent(
reference='foo', reference='foo',
...@@ -173,6 +218,61 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin): ...@@ -173,6 +218,61 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin):
self.assertEqual(other_address.getNetmask(), 'f') self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkInterface(), 'bar') self.assertEqual(other_address.getNetworkInterface(), 'bar')
def test_CreateMultiplePartition_TapNetworkInformation(self):
partition = self.computer.newContent(
reference='foo',
portal_type='Computer Partition',
)
# No address in the empty partition
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 0)
parameter_dict = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
},{
'addr': 'e',
'netmask': 'f',
}],
'tap': {'name': 'bar',
'ipv4_addr': 'g',
'ipv4_netmask': 'h',
'ipv4_network': 'i',
'ipv4_gateway': 'j'},
}],
'address': 'a',
'netmask': 'b',
}
self.computer.Computer_updateFromDict(parameter_dict)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 3)
default_address = [x for x in address_list \
if x.getId() == 'default_network_address'][0]
self.assertEqual(default_address.getIpAddress(), 'c')
self.assertEqual(default_address.getNetmask(), 'd')
self.assertEqual(default_address.getNetworkInterface(), 'bar')
other_address_list = [x for x in address_list \
if x.getId() != 'default_network_address']
other_address_list.sort(key=lambda x: {1: 1, 0: 2}[int(x.getNetworkInterface() == 'bar')])
other_address = other_address_list[0]
self.assertEqual(other_address.getIpAddress(), 'e')
self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkInterface(), 'bar')
other_address1 = other_address_list[1]
self.assertEqual(other_address1.getIpAddress(), 'g')
self.assertEqual(other_address1.getNetmask(), 'h')
self.assertEqual(other_address1.getNetworkAddress(), 'i')
self.assertEqual(other_address1.getGatewayIpAddress(), 'j')
self.assertEqual(other_address1.getNetworkInterface(), 'route_bar')
def test_UpdateSinglePartitionNetworkInformation(self): def test_UpdateSinglePartitionNetworkInformation(self):
partition = self.computer.newContent( partition = self.computer.newContent(
reference='foo', reference='foo',
...@@ -210,6 +310,55 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin): ...@@ -210,6 +310,55 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin):
self.assertEqual(address.getNetworkInterface(), 'bar') self.assertEqual(address.getNetworkInterface(), 'bar')
self.assertEqual(address.getId(), 'foo') self.assertEqual(address.getId(), 'foo')
def test_UpdateSinglePartition_TapNetworkInformation(self):
partition = self.computer.newContent(
reference='foo',
portal_type='Computer Partition',
)
# No address in the empty partition
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 0)
address = partition.newContent(
id ='foo',
portal_type='Internet Protocol Address',
)
parameter_dict = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
}],
'tap': {'name': 'bar',
'ipv4_addr': 'e',
'ipv4_netmask': 'f',
'ipv4_network': 'g',
'ipv4_gateway': 'h'},
}],
'address': 'a',
'netmask': 'b',
}
self.computer.Computer_updateFromDict(parameter_dict)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 2)
address_list.sort(key=lambda x: {1: 1, 0: 2}[int(x.getNetworkInterface() == 'bar')])
address = address_list[0]
self.assertEqual(address.getIpAddress(), 'c')
self.assertEqual(address.getNetmask(), 'd')
self.assertEqual(address.getNetworkInterface(), 'bar')
self.assertEqual(address.getId(), 'foo')
other_address = address_list[1]
self.assertEqual(other_address.getIpAddress(), 'e')
self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkAddress(), 'g')
self.assertEqual(other_address.getGatewayIpAddress(), 'h')
self.assertEqual(other_address.getNetworkInterface(), 'route_bar')
def test_UpdateMultiplePartitionNetworkInformation(self): def test_UpdateMultiplePartitionNetworkInformation(self):
partition = self.computer.newContent( partition = self.computer.newContent(
reference='foo', reference='foo',
...@@ -258,6 +407,143 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin): ...@@ -258,6 +407,143 @@ class TestSlapOSCoreComputerUpdateFromDict(testSlapOSMixin):
self.assertEqual(other_address.getNetmask(), 'f') self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkInterface(), 'bar') self.assertEqual(other_address.getNetworkInterface(), 'bar')
def test_UpdateMultiplePartition_TapNetworkInformation(self):
partition = self.computer.newContent(
reference='foo',
portal_type='Computer Partition',
)
# No address in the empty partition
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 0)
other_address = partition.newContent(
id ='foo',
portal_type='Internet Protocol Address',
)
other_address2 = partition.newContent(
id ='route_foo',
portal_type='Internet Protocol Address',
)
default_address = partition.newContent(
id ='default_network_interface',
portal_type='Internet Protocol Address',
)
parameter_dict = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
},{
'addr': 'e',
'netmask': 'f',
}],
'tap': {'name': 'bar',
'ipv4_addr': 'g',
'ipv4_netmask': 'h',
'ipv4_network': 'i',
'ipv4_gateway': 'j'},
}],
'address': 'a',
'netmask': 'b',
}
self.computer.Computer_updateFromDict(parameter_dict)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 3)
# First address should go to the default one
self.assertEqual(default_address.getIpAddress(), 'c')
self.assertEqual(default_address.getNetmask(), 'd')
self.assertEqual(default_address.getNetworkInterface(), 'bar')
other_address_list = [x for x in address_list \
if x.getId() != 'default_network_interface']
other_address_list.sort(
key=lambda x: {1: 1, 0: 2}[int(x.getNetworkInterface() == 'bar')])
other_address = other_address_list[0]
self.assertEqual(other_address.getIpAddress(), 'e')
self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkInterface(), 'bar')
other_address = other_address_list[1]
self.assertEqual(other_address.getIpAddress(), 'g')
self.assertEqual(other_address.getNetmask(), 'h')
self.assertEqual(other_address.getNetworkAddress(), 'i')
self.assertEqual(other_address.getGatewayIpAddress(), 'j')
self.assertEqual(other_address.getNetworkInterface(), 'route_bar')
def test_removePartitionTapNetworkInformation(self):
partition = self.computer.newContent(
reference='foo',
portal_type='Computer Partition',
)
# No address in the empty partition
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 0)
parameter_dict = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
}],
'tap': {'name': 'bar',
'ipv4_addr': 'e',
'ipv4_netmask': 'f',
'ipv4_network': 'g',
'ipv4_gateway': 'h'},
}],
'address': 'a',
'netmask': 'b',
}
parameter_dict2 = {
'partition_list': [{
'reference': 'foo',
'address_list': [{
'addr': 'c',
'netmask': 'd',
}],
'tap': {'name': 'bar'},
}],
'address': 'a',
'netmask': 'b',
}
self.computer.Computer_updateFromDict(parameter_dict)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
address_list.sort(
key=lambda x: {1: 1, 0: 2}[int(x.getId() == 'default_network_address')])
self.assertEqual(len(address_list), 2)
address = address_list[0]
self.assertEqual(address.getIpAddress(), 'c')
self.assertEqual(address.getNetmask(), 'd')
self.assertEqual(address.getNetworkInterface(), 'bar')
self.assertEqual(address.getId(), 'default_network_address')
other_address = address_list[1]
self.assertEqual(other_address.getIpAddress(), 'e')
self.assertEqual(other_address.getNetmask(), 'f')
self.assertEqual(other_address.getNetworkAddress(), 'g')
self.assertEqual(other_address.getGatewayIpAddress(), 'h')
self.assertEqual(other_address.getNetworkInterface(), 'route_bar')
self.computer.Computer_updateFromDict(parameter_dict2)
address_list = partition.contentValues(
portal_type='Internet Protocol Address')
self.assertEqual(len(address_list), 1)
self.assertEqual(address_list[0].getIpAddress(), 'c')
self.assertEqual(address_list[0].getNetmask(), 'd')
self.assertEqual(address_list[0].getNetworkInterface(), 'bar')
self.assertEqual(address_list[0].getId(), 'default_network_address')
def test_RemoveSinglePartitionNetworkInformation(self): def test_RemoveSinglePartitionNetworkInformation(self):
partition = self.computer.newContent( partition = self.computer.newContent(
reference='foo', reference='foo',
......
...@@ -6,10 +6,22 @@ ...@@ -6,10 +6,22 @@
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>testSlapOSSlapToolComputerUpdateFromDict</string> </value> <value> <string>testSlapOSSlapToolComputerUpdateFromDict</string> </value>
</item> </item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>test.erp5.testSlapOSSlapToolComputerUpdateFromDict</string> </value> <value> <string>test.erp5.testSlapOSSlapToolComputerUpdateFromDict</string> </value>
...@@ -34,10 +46,11 @@ ...@@ -34,10 +46,11 @@
<key> <string>text_content_warning_message</string> </key> <key> <string>text_content_warning_message</string> </key>
<value> <value>
<tuple> <tuple>
<string>W:274, 4: Unused variable \'default_address\' (unused-variable)</string> <string>W:423, 4: Unused variable \'other_address2\' (unused-variable)</string>
<string>W:270, 4: Unused variable \'other_address\' (unused-variable)</string> <string>W:560, 4: Unused variable \'default_address\' (unused-variable)</string>
<string>W:315, 4: Unused variable \'default_address\' (unused-variable)</string> <string>W:556, 4: Unused variable \'other_address\' (unused-variable)</string>
<string>W:311, 4: Unused variable \'other_address\' (unused-variable)</string> <string>W:601, 4: Unused variable \'default_address\' (unused-variable)</string>
<string>W:597, 4: Unused variable \'other_address\' (unused-variable)</string>
</tuple> </tuple>
</value> </value>
</item> </item>
...@@ -48,13 +61,28 @@ ...@@ -48,13 +61,28 @@
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="PersistentMapping" module="Persistence.mapping"/> <global name="PersistentMapping" module="Persistence.mapping"/>
</pickle> </pickle>
...@@ -67,7 +95,7 @@ ...@@ -67,7 +95,7 @@
<item> <item>
<key> <string>component_validation_workflow</string> </key> <key> <string>component_validation_workflow</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -76,7 +104,7 @@ ...@@ -76,7 +104,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="3" aka="AAAAAAAAAAM="> <record id="4" aka="AAAAAAAAAAQ=">
<pickle> <pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/> <global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle> </pickle>
......
...@@ -1429,9 +1429,17 @@ class SlapTool(BaseTool): ...@@ -1429,9 +1429,17 @@ class SlapTool(BaseTool):
ip_list = [] ip_list = []
for internet_protocol_address in computer_partition.contentValues(portal_type='Internet Protocol Address'): for internet_protocol_address in computer_partition.contentValues(portal_type='Internet Protocol Address'):
ip_list.append(( # XXX - There is new values, and we must keep compatibility
address_tuple = (
internet_protocol_address.getNetworkInterface('').decode("UTF-8"), internet_protocol_address.getNetworkInterface('').decode("UTF-8"),
internet_protocol_address.getIpAddress().decode("UTF-8"))) internet_protocol_address.getIpAddress().decode("UTF-8"))
if internet_protocol_address.getGatewayIpAddress('') and \
internet_protocol_address.getNetmask(''):
address_tuple = address_tuple + (
internet_protocol_address.getGatewayIpAddress().decode("UTF-8"),
internet_protocol_address.getNetmask().decode("UTF-8"),
internet_protocol_address.getNetworkAddress('').decode("UTF-8"))
ip_list.append(address_tuple)
slave_instance_list = [] slave_instance_list = []
if (software_instance.getPortalType() == "Software Instance"): if (software_instance.getPortalType() == "Software Instance"):
......
...@@ -152,6 +152,36 @@ def netmaskToPrefixIPv6(netmask): ...@@ -152,6 +152,36 @@ def netmaskToPrefixIPv6(netmask):
return netaddr.strategy.ipv6.netmask_to_prefix[ return netaddr.strategy.ipv6.netmask_to_prefix[
netaddr.strategy.ipv6.str_to_int(netmask)] netaddr.strategy.ipv6.str_to_int(netmask)]
def getIfaceAddressIPv4(iface):
"""return dict containing ipv4 address netmask, network and broadcast address
of interface"""
if not iface in netifaces.interfaces():
raise ValueError('Could not find interface called %s to use as gateway ' \
'for tap network' % iface)
try:
addresses_list = netifaces.ifaddresses(iface)[socket.AF_INET]
if len (addresses_list) > 0:
addresses = addresses_list[0].copy()
addresses['network'] = str(netaddr.IPNetwork('%s/%s' % (addresses['addr'],
addresses['netmask'])).cidr.network)
return addresses
else:
return {}
except KeyError:
raise KeyError('Could not find IPv4 adress on interface %s.' % iface)
def getIPv4SubnetAddressRange(ip_address, mask, size):
"""Check if a given ipaddress can be used to create 'size'
host ip address, then return list of ip address in the subnet"""
ip = netaddr.IPNetwork('%s/%s' % (ip_address, mask))
# Delete network and default ip_address from the list
ip_list = [x for x in sorted(list(ip))
if str(x) != ip_address and x.value != ip.cidr.network.value]
if len(ip_list) < size:
raise ValueError('Could not create %s tap interfaces from address %s.' % (
size, ip_address))
return ip_list
def _getDict(obj): def _getDict(obj):
""" """
...@@ -188,9 +218,11 @@ class Computer(object): ...@@ -188,9 +218,11 @@ class Computer(object):
"Object representing the computer" "Object representing the computer"
instance_root = None instance_root = None
software_root = None software_root = None
instance_storage_home = None
def __init__(self, reference, interface=None, addr=None, netmask=None, def __init__(self, reference, interface=None, addr=None, netmask=None,
ipv6_interface=None, software_user='slapsoft'): ipv6_interface=None, software_user='slapsoft',
tap_gateway_interface=None):
""" """
Attributes: Attributes:
reference: String, the reference of the computer. reference: String, the reference of the computer.
...@@ -203,6 +235,7 @@ class Computer(object): ...@@ -203,6 +235,7 @@ class Computer(object):
self.netmask = netmask self.netmask = netmask
self.ipv6_interface = ipv6_interface self.ipv6_interface = ipv6_interface
self.software_user = software_user self.software_user = software_user
self.tap_gateway_interface = tap_gateway_interface
def __getinitargs__(self): def __getinitargs__(self):
return (self.reference, self.interface) return (self.reference, self.interface)
...@@ -317,7 +350,7 @@ class Computer(object): ...@@ -317,7 +350,7 @@ class Computer(object):
archive.writestr(saved_filename, xml_content, zipfile.ZIP_DEFLATED) archive.writestr(saved_filename, xml_content, zipfile.ZIP_DEFLATED)
@classmethod @classmethod
def load(cls, path_to_xml, reference, ipv6_interface): def load(cls, path_to_xml, reference, ipv6_interface, tap_gateway_interface):
""" """
Create a computer object from a valid xml file. Create a computer object from a valid xml file.
...@@ -338,6 +371,7 @@ class Computer(object): ...@@ -338,6 +371,7 @@ class Computer(object):
netmask=dumped_dict['netmask'], netmask=dumped_dict['netmask'],
ipv6_interface=ipv6_interface, ipv6_interface=ipv6_interface,
software_user=dumped_dict.get('software_user', 'slapsoft'), software_user=dumped_dict.get('software_user', 'slapsoft'),
tap_gateway_interface=tap_gateway_interface,
) )
for partition_dict in dumped_dict['partition_list']: for partition_dict in dumped_dict['partition_list']:
...@@ -349,10 +383,15 @@ class Computer(object): ...@@ -349,10 +383,15 @@ class Computer(object):
if partition_dict['tap']: if partition_dict['tap']:
tap = Tap(partition_dict['tap']['name']) tap = Tap(partition_dict['tap']['name'])
tap.ipv4_addr = partition_dict['tap'].get('ipv4_addr', '')
tap.ipv4_netmask = partition_dict['tap'].get('ipv4_netmask', '')
tap.ipv4_gateway = partition_dict['tap'].get('ipv4_gateway', '')
tap.ipv4_network = partition_dict['tap'].get('ipv4_network', '')
else: else:
tap = Tap(partition_dict['reference']) tap = Tap(partition_dict['reference'])
address_list = partition_dict['address_list'] address_list = partition_dict['address_list']
external_storage_list = partition_dict.get('external_storage_list', [])
partition = Partition( partition = Partition(
reference=partition_dict['reference'], reference=partition_dict['reference'],
...@@ -360,6 +399,7 @@ class Computer(object): ...@@ -360,6 +399,7 @@ class Computer(object):
user=user, user=user,
address_list=address_list, address_list=address_list,
tap=tap, tap=tap,
external_storage_list=external_storage_list,
) )
computer.partition_list.append(partition) computer.partition_list.append(partition)
...@@ -431,6 +471,23 @@ class Computer(object): ...@@ -431,6 +471,23 @@ class Computer(object):
os.chown(slapsoft.path, slapsoft_pw.pw_uid, slapsoft_pw.pw_gid) os.chown(slapsoft.path, slapsoft_pw.pw_uid, slapsoft_pw.pw_gid)
os.chmod(self.software_root, 0o755) os.chmod(self.software_root, 0o755)
# get list of instance external storage if exist
instance_external_list = []
if self.instance_storage_home:
# XXX - Hard limit for storage number to 4
for i in range(1, 5):
storage_path = os.path.join(self.instance_storage_home, 'data%s' % i)
if os.path.exists(storage_path):
instance_external_list.append(storage_path)
tap_address_list = []
if alter_network and self.tap_gateway_interface and create_tap:
gateway_addr_dict = getIfaceAddressIPv4(self.tap_gateway_interface)
tap_address_list = getIPv4SubnetAddressRange(gateway_addr_dict['addr'],
gateway_addr_dict['netmask'],
len(self.partition_list))
assert(len(self.partition_list) <= len(tap_address_list))
if alter_network: if alter_network:
self._speedHackAddAllOldIpsToInterface() self._speedHackAddAllOldIpsToInterface()
...@@ -440,6 +497,8 @@ class Computer(object): ...@@ -440,6 +497,8 @@ class Computer(object):
partition.path = os.path.join(self.instance_root, partition.reference) partition.path = os.path.join(self.instance_root, partition.reference)
partition.user.setPath(partition.path) partition.user.setPath(partition.path)
partition.user.additional_group_list = [slapsoft.name] partition.user.additional_group_list = [slapsoft.name]
partition.external_storage_list = ['%s/%s' % (path, partition.reference)
for path in instance_external_list]
if alter_user: if alter_user:
partition.user.create() partition.user.create()
...@@ -456,11 +515,23 @@ class Computer(object): ...@@ -456,11 +515,23 @@ class Computer(object):
partition.tap.createWithOwner(owner, attach_to_tap=True) partition.tap.createWithOwner(owner, attach_to_tap=True)
else: else:
partition.tap.createWithOwner(owner) partition.tap.createWithOwner(owner)
# If tap_gateway_interface is specified, we don't add tap to bridge
# but we create route for this tap
if not self.tap_gateway_interface:
self.interface.addTap(partition.tap) self.interface.addTap(partition.tap)
else:
next_ipv4_addr = '%s' % tap_address_list.pop(0)
if not partition.tap.ipv4_addr:
# define new ipv4 address for this tap
partition.tap.ipv4_addr = next_ipv4_addr
partition.tap.ipv4_netmask = gateway_addr_dict['netmask']
partition.tap.ipv4_gateway = gateway_addr_dict['addr']
partition.tap.ipv4_network = gateway_addr_dict['network']
partition.tap.createRoutes()
# Reconstructing partition's directory # Reconstructing partition's directory
partition.createPath(alter_user) partition.createPath(alter_user)
partition.createExternalPath(alter_user)
# Reconstructing partition's address # Reconstructing partition's address
# There should be two addresses on each Computer Partition: # There should be two addresses on each Computer Partition:
...@@ -506,7 +577,7 @@ class Computer(object): ...@@ -506,7 +577,7 @@ class Computer(object):
class Partition(object): class Partition(object):
"Represent a computer partition" "Represent a computer partition"
def __init__(self, reference, path, user, address_list, tap): def __init__(self, reference, path, user, address_list, tap, external_storage_list=[]):
""" """
Attributes: Attributes:
reference: String, the name of the partition. reference: String, the name of the partition.
...@@ -514,6 +585,7 @@ class Partition(object): ...@@ -514,6 +585,7 @@ class Partition(object):
user: User, the user linked to this partition. user: User, the user linked to this partition.
address_list: List of associated IP addresses. address_list: List of associated IP addresses.
tap: Tap, the tap interface linked to this partition. tap: Tap, the tap interface linked to this partition.
external_storage_list: Base path list of folder to format for data storage
""" """
self.reference = str(reference) self.reference = str(reference)
...@@ -521,6 +593,7 @@ class Partition(object): ...@@ -521,6 +593,7 @@ class Partition(object):
self.user = user self.user = user
self.address_list = address_list or [] self.address_list = address_list or []
self.tap = tap self.tap = tap
self.external_storage_list = []
def __getinitargs__(self): def __getinitargs__(self):
return (self.reference, self.path, self.user, self.address_list, self.tap) return (self.reference, self.path, self.user, self.address_list, self.tap)
...@@ -540,6 +613,21 @@ class Partition(object): ...@@ -540,6 +613,21 @@ class Partition(object):
os.chown(self.path, owner_pw.pw_uid, owner_pw.pw_gid) os.chown(self.path, owner_pw.pw_uid, owner_pw.pw_gid)
os.chmod(self.path, 0o750) os.chmod(self.path, 0o750)
def createExternalPath(self, alter_user=True):
"""
Create and external directory of the partition, assign to the partition user
and give it the 750 permission. In case if path exists just modifies it.
"""
for path in self.external_storage_list:
storage_path = os.path.abspath(path)
owner = self.user if self.user else User('root')
if not os.path.exists(storage_path):
os.mkdir(storage_path, 0o750)
if alter_user:
owner_pw = pwd.getpwnam(owner.name)
os.chown(storage_path, owner_pw.pw_uid, owner_pw.pw_gid)
os.chmod(storage_path, 0o750)
class User(object): class User(object):
"""User: represent and manipulate a user on the system.""" """User: represent and manipulate a user on the system."""
...@@ -617,9 +705,16 @@ class Tap(object): ...@@ -617,9 +705,16 @@ class Tap(object):
""" """
Attributes: Attributes:
tap_name: String, the name of the tap interface. tap_name: String, the name of the tap interface.
ipv4_address: String, local ipv4 to route to this tap
ipv4_network: String, netmask to use when configure route for this tap
gateway_ipv4: String, ipv4 of gateway to be used to reach local network
""" """
self.name = str(tap_name) self.name = str(tap_name)
self.ipv4_addr = ""
self.ipv4_netmask = ""
self.ipv4_gateway = ""
self.ipv4_network = ""
def __getinitargs__(self): def __getinitargs__(self):
return (self.name,) return (self.name,)
...@@ -698,6 +793,19 @@ class Tap(object): ...@@ -698,6 +793,19 @@ class Tap(object):
if attach_to_tap: if attach_to_tap:
threading.Thread(target=self.attach).start() threading.Thread(target=self.attach).start()
def createRoutes(self):
"""
Configure ipv4 route to reach this interface from local network
"""
if self.ipv4_addr:
# Check if this route exits
code, result = callAndRead(['ip', 'route', 'show', self.ipv4_addr])
if code == 0 and self.ipv4_addr in result and self.name in result:
return
callAndRead(['route', 'add', '-host', self.ipv4_addr, 'dev', self.name])
else:
raise ValueError("%s should not be empty. No ipv4 address assigned to %s" %
(self.ipv4_addr, self.name))
class Interface(object): class Interface(object):
"""Represent a network interface on the system""" """Represent a network interface on the system"""
...@@ -980,6 +1088,7 @@ def parse_computer_definition(conf, definition_path): ...@@ -980,6 +1088,7 @@ def parse_computer_definition(conf, definition_path):
netmask=netmask, netmask=netmask,
ipv6_interface=conf.ipv6_interface, ipv6_interface=conf.ipv6_interface,
software_user=computer_definition.get('computer', 'software_user'), software_user=computer_definition.get('computer', 'software_user'),
tap_gateway_interface=conf.tap_gateway_interface,
) )
partition_list = [] partition_list = []
for partition_number in range(int(conf.partition_amount)): for partition_number in range(int(conf.partition_amount)):
...@@ -1011,7 +1120,8 @@ def parse_computer_xml(conf, xml_path): ...@@ -1011,7 +1120,8 @@ def parse_computer_xml(conf, xml_path):
conf.logger.debug('Loading previous computer data from %r' % xml_path) conf.logger.debug('Loading previous computer data from %r' % xml_path)
computer = Computer.load(xml_path, computer = Computer.load(xml_path,
reference=conf.computer_id, reference=conf.computer_id,
ipv6_interface=conf.ipv6_interface) ipv6_interface=conf.ipv6_interface,
tap_gateway_interface=conf.tap_gateway_interface)
# Connect to the interface defined by the configuration # Connect to the interface defined by the configuration
computer.interface = interface computer.interface = interface
else: else:
...@@ -1024,6 +1134,7 @@ def parse_computer_xml(conf, xml_path): ...@@ -1024,6 +1134,7 @@ def parse_computer_xml(conf, xml_path):
netmask=None, netmask=None,
ipv6_interface=conf.ipv6_interface, ipv6_interface=conf.ipv6_interface,
software_user=conf.software_user, software_user=conf.software_user,
tap_gateway_interface=conf.tap_gateway_interface,
) )
partition_amount = int(conf.partition_amount) partition_amount = int(conf.partition_amount)
...@@ -1095,6 +1206,7 @@ def do_format(conf): ...@@ -1095,6 +1206,7 @@ def do_format(conf):
computer.instance_root = conf.instance_root computer.instance_root = conf.instance_root
computer.software_root = conf.software_root computer.software_root = conf.software_root
computer.instance_storage_home = conf.instance_storage_home
conf.logger.info('Updating computer') conf.logger.info('Updating computer')
address = computer.getAddress(conf.create_tap) address = computer.getAddress(conf.create_tap)
computer.address = address['addr'] computer.address = address['addr']
...@@ -1134,7 +1246,9 @@ class FormatConfig(object): ...@@ -1134,7 +1246,9 @@ class FormatConfig(object):
output_definition_file = None output_definition_file = None
dry_run = None dry_run = None
software_user = None software_user = None
tap_gateway_interface = None
use_unique_local_address_block = None use_unique_local_address_block = None
instance_storage_home = None
def __init__(self, logger): def __init__(self, logger):
self.logger = logger self.logger = logger
...@@ -1202,6 +1316,8 @@ class FormatConfig(object): ...@@ -1202,6 +1316,8 @@ class FormatConfig(object):
self.software_user = 'slapsoft' self.software_user = 'slapsoft'
if self.create_tap is None: if self.create_tap is None:
self.create_tap = True self.create_tap = True
if self.tap_gateway_interface is None:
self.tap_gateway_interface = ''
if self.use_unique_local_address_block is None: if self.use_unique_local_address_block is None:
self.use_unique_local_address_block = False self.use_unique_local_address_block = False
...@@ -1225,6 +1341,8 @@ class FormatConfig(object): ...@@ -1225,6 +1341,8 @@ class FormatConfig(object):
self.checkRequiredBinary(['groupadd', 'useradd', 'usermod']) self.checkRequiredBinary(['groupadd', 'useradd', 'usermod'])
if self.create_tap: if self.create_tap:
self.checkRequiredBinary([['tunctl', '-d']]) self.checkRequiredBinary([['tunctl', '-d']])
if self.tap_gateway_interface:
self.checkRequiredBinary(['route'])
if self.alter_network: if self.alter_network:
self.checkRequiredBinary(['ip']) self.checkRequiredBinary(['ip'])
...@@ -1292,3 +1410,4 @@ def tracing_monkeypatch(conf): ...@@ -1292,3 +1410,4 @@ def tracing_monkeypatch(conf):
conf.logger.debug(' '.join(argument_list)) conf.logger.debug(' '.join(argument_list))
return dry_callAndRead(argument_list, raise_on_error) return dry_callAndRead(argument_list, raise_on_error)
callAndRead = logging_callAndRead callAndRead = logging_callAndRead
...@@ -57,6 +57,8 @@ WATCHDOG_MARK = '-on-watch' ...@@ -57,6 +57,8 @@ WATCHDOG_MARK = '-on-watch'
REQUIRED_COMPUTER_PARTITION_PERMISSION = 0o750 REQUIRED_COMPUTER_PARTITION_PERMISSION = 0o750
CP_STORAGE_FOLDER_NAME = 'DATA'
# XXX not very clean. this is changed when testing # XXX not very clean. this is changed when testing
PROGRAM_PARTITION_TEMPLATE = pkg_resources.resource_stream(__name__, PROGRAM_PARTITION_TEMPLATE = pkg_resources.resource_stream(__name__,
'templates/program_partition_supervisord.conf.in').read() 'templates/program_partition_supervisord.conf.in').read()
...@@ -341,7 +343,8 @@ class Partition(object): ...@@ -341,7 +343,8 @@ class Partition(object):
logger, logger,
certificate_repository_path=None, certificate_repository_path=None,
retention_delay='0', retention_delay='0',
instance_min_free_space=None instance_min_free_space=None,
instance_storage_home=''
): ):
"""Initialisation of class parameters""" """Initialisation of class parameters"""
self.buildout = buildout self.buildout = buildout
...@@ -358,6 +361,7 @@ class Partition(object): ...@@ -358,6 +361,7 @@ class Partition(object):
self.partition_id = partition_id self.partition_id = partition_id
self.server_url = server_url self.server_url = server_url
self.software_release_url = software_release_url self.software_release_url = software_release_url
self.instance_storage_home = instance_storage_home
self.key_file = '' self.key_file = ''
self.cert_file = '' self.cert_file = ''
...@@ -512,6 +516,7 @@ class Partition(object): ...@@ -512,6 +516,7 @@ class Partition(object):
'software_release_url': self.software_release_url, 'software_release_url': self.software_release_url,
'key_file': self.key_file, 'key_file': self.key_file,
'cert_file': self.cert_file, 'cert_file': self.cert_file,
'storage_home': self.instance_storage_home,
} }
open(config_location, 'w').write(buildout_text) open(config_location, 'w').write(buildout_text)
os.chmod(config_location, 0o640) os.chmod(config_location, 0o640)
...@@ -684,12 +689,22 @@ class Partition(object): ...@@ -684,12 +689,22 @@ class Partition(object):
sr_symlink = os.path.join(self.instance_path, 'software_release') sr_symlink = os.path.join(self.instance_path, 'software_release')
if os.path.islink(sr_symlink): if os.path.islink(sr_symlink):
os.unlink(sr_symlink) os.unlink(sr_symlink)
data_base_link = os.path.join(self.instance_path, CP_STORAGE_FOLDER_NAME)
if self.instance_storage_home and os.path.exists(data_base_link) and \
os.path.isdir(data_base_link):
for filename in os.listdir(data_base_link):
data_symlink = os.path.join(data_base_link, filename)
partition_data_path = os.path.join(self.instance_storage_home,
filename, self.partition_id)
if os.path.lexists(data_symlink):
os.unlink(data_symlink)
if os.path.exists(partition_data_path):
self.cleanupFolder(partition_data_path)
self.cleanupFolder(self.instance_path)
# Cleanup all Data storage location of this partition
for root, dirs, file_list in os.walk(self.instance_path):
for directory in dirs:
shutil.rmtree(os.path.join(self.instance_path, directory))
for file in file_list:
os.remove(os.path.join(self.instance_path, file))
if os.path.exists(self.supervisord_partition_configuration_path): if os.path.exists(self.supervisord_partition_configuration_path):
os.remove(self.supervisord_partition_configuration_path) os.remove(self.supervisord_partition_configuration_path)
...@@ -699,6 +714,15 @@ class Partition(object): ...@@ -699,6 +714,15 @@ class Partition(object):
return True return True
def cleanupFolder(self, folder_path):
"""Delete all files and folders in a specified directory
"""
for root, dirs, file_list in os.walk(folder_path):
for directory in dirs:
shutil.rmtree(os.path.join(folder_path, directory))
for file in file_list:
os.remove(os.path.join(folder_path, file))
def fetchInformations(self): def fetchInformations(self):
"""Fetch usage informations with buildout, returns it. """Fetch usage informations with buildout, returns it.
""" """
......
...@@ -232,7 +232,8 @@ def create_slapgrid_object(options, logger): ...@@ -232,7 +232,8 @@ def create_slapgrid_object(options, logger):
# Try to fetch from deprecated argument # Try to fetch from deprecated argument
computer_partition_filter_list=op.get('only-cp', op.get('only_cp')), computer_partition_filter_list=op.get('only-cp', op.get('only_cp')),
software_min_free_space=software_min_free_space, software_min_free_space=software_min_free_space,
instance_min_free_space=instance_min_free_space) instance_min_free_space=instance_min_free_space,
instance_storage_home=op.get('instance_storage_home'))
def check_required_only_partitions(existing, required): def check_required_only_partitions(existing, required):
...@@ -286,6 +287,7 @@ class Slapgrid(object): ...@@ -286,6 +287,7 @@ class Slapgrid(object):
computer_partition_filter_list=None, computer_partition_filter_list=None,
software_min_free_space=None, software_min_free_space=None,
instance_min_free_space=None, instance_min_free_space=None,
instance_storage_home=None,
): ):
"""Makes easy initialisation of class parameters""" """Makes easy initialisation of class parameters"""
# Parses arguments # Parses arguments
...@@ -337,6 +339,10 @@ class Slapgrid(object): ...@@ -337,6 +339,10 @@ class Slapgrid(object):
self.maximum_periodicity = maximum_periodicity self.maximum_periodicity = maximum_periodicity
self.software_min_free_space = software_min_free_space self.software_min_free_space = software_min_free_space
self.instance_min_free_space = instance_min_free_space self.instance_min_free_space = instance_min_free_space
if instance_storage_home:
self.instance_storage_home = os.path.abspath(instance_storage_home)
else:
self.instance_storage_home = ""
def _getWatchdogLine(self): def _getWatchdogLine(self):
invocation_list = [WATCHDOG_PATH] invocation_list = [WATCHDOG_PATH]
...@@ -644,6 +650,7 @@ class Slapgrid(object): ...@@ -644,6 +650,7 @@ class Slapgrid(object):
logger=self.logger, logger=self.logger,
retention_delay=retention_delay, retention_delay=retention_delay,
instance_min_free_space=self.instance_min_free_space, instance_min_free_space=self.instance_min_free_space,
instance_storage_home=self.instance_storage_home,
) )
computer_partition_state = computer_partition.getState() computer_partition_state = computer_partition.getState()
...@@ -1102,6 +1109,7 @@ class Slapgrid(object): ...@@ -1102,6 +1109,7 @@ class Slapgrid(object):
certificate_repository_path=self.certificate_repository_path, certificate_repository_path=self.certificate_repository_path,
buildout=self.buildout, buildout=self.buildout,
logger=self.logger, logger=self.logger,
instance_storage_home=self.instance_storage_home,
) )
local_partition.stop() local_partition.stop()
try: try:
......
...@@ -22,4 +22,7 @@ server_url = %(server_url)s ...@@ -22,4 +22,7 @@ server_url = %(server_url)s
software_release_url = %(software_release_url)s software_release_url = %(software_release_url)s
key_file = %(key_file)s key_file = %(key_file)s
cert_file = %(cert_file)s cert_file = %(cert_file)s
[storage-configuration]
storage-home = %(storage_home)s
# This is end of zc.builodout profile's tail added by slapgrid # This is end of zc.builodout profile's tail added by slapgrid
...@@ -94,6 +94,10 @@ class FakeCallAndRead: ...@@ -94,6 +94,10 @@ class FakeCallAndRead:
elif argument_list[:3] == ['ip', 'addr', 'list'] or \ elif argument_list[:3] == ['ip', 'addr', 'list'] or \
argument_list[:4] == ['ip', '-6', 'addr', 'list']: argument_list[:4] == ['ip', '-6', 'addr', 'list']:
retval = 0, str(INTERFACE_DICT) retval = 0, str(INTERFACE_DICT)
elif argument_list[:3] == ['ip', 'route', 'show']:
retval = 0, 'OK'
elif argument_list[:3] == ['route', 'add', '-host']:
retval = 0, 'OK'
self.external_command_list.append(' '.join(argument_list)) self.external_command_list.append(' '.join(argument_list))
return retval return retval
...@@ -441,6 +445,56 @@ class TestComputer(SlapformatMixin): ...@@ -441,6 +445,56 @@ class TestComputer(SlapformatMixin):
], ],
self.fakeCallAndRead.external_command_list) self.fakeCallAndRead.external_command_list)
def test_construct_prepared_tap_no_alter_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='iface',
ipv4_local_network='127.0.0.1/16'),
tap_gateway_interface='eth1')
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
global USER_LIST
USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['iface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
INTERFACE_DICT['eth1'] = {
socket.AF_INET: [{'addr': '10.8.0.1', 'broadcast': '10.8.0.254',
'netmask': '255.255.255.0'}]
}
computer.construct(alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list iface',
'tunctl -t tap -u testuser',
'ip link set tap up',
'ip route show 10.8.0.2',
'route add -host 10.8.0.2 dev tap',
'ip addr add ip/255.255.255.255 dev iface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev iface',
'ip -6 addr list iface'
],
self.fakeCallAndRead.external_command_list)
self.assertEqual(partition.tap.ipv4_addr, '10.8.0.2')
self.assertEqual(partition.tap.ipv4_netmask, '255.255.255.0')
self.assertEqual(partition.tap.ipv4_gateway, '10.8.0.1')
self.assertEqual(partition.tap.ipv4_network, '10.8.0.0')
@unittest.skip("Not implemented") @unittest.skip("Not implemented")
def test_construct_prepared_no_alter_network(self): def test_construct_prepared_no_alter_network(self):
computer = slapos.format.Computer('computer', computer = slapos.format.Computer('computer',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment