migrate-comar-network-profiles 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # Copyright 2009-2010 TUBITAK/UEKAE
  4. # Licensed under the GNU General Public License, version 2.
  5. # See the file http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
  6. #
  7. #
  8. #
  9. # A migration script to transform COMAR based network profiles into NetworkManager based network profiles.
  10. #
  11. # The main purpose of the script is help Pardus 2009 users to be able to use their network profiles in Pardus 2011
  12. # without any effort when they upgrade their system.
  13. #
  14. # A few words about how stuff works. COMAR based network manager keeps profile settings under /etc/network with two
  15. # files: net_tools which includes ethernet profile settings and wireless_tools which includes wireless profile settings.
  16. # On the other hand, NetworkManager keeps each profile settings in an individual file under /etc/NetworkManager/system-connections.
  17. #
  18. # This script simply reads your old profile files and try to create a new profile file compatible with NetworkManager for each profile.
  19. #
  20. #
  21. import os
  22. import ConfigParser
  23. default_nameservers = []
  24. default_resolv_conf_file = "/etc/resolv.default.conf"
  25. lan_config_file = "/etc/network/net_tools"
  26. wlan_config_file = "/etc/network/wireless_tools"
  27. migration_sign = "/etc/network/.migrated"
  28. NetworkManager_conf_dir = "/etc/NetworkManager/system-connections"
  29. def is_migrated_before():
  30. """Make this tool run once; so after migration, touch a file named '.migrated'
  31. under old network manager profiles dir. Check it first and then try to migrate profiles.
  32. """
  33. global migration_sign
  34. if os.path.exists(migration_sign):
  35. return True
  36. else:
  37. return False
  38. def get_default_nameservers():
  39. """Read once default name servers in resolve.default.conf, if 'name_mode' in
  40. given profile was set to default, supply these values in the new profile file
  41. """
  42. global default_nameservers
  43. if len(default_nameservers) == 0:
  44. if os.path.exists(default_resolv_conf_file):
  45. with open(default_resolv_conf_file) as f:
  46. nameservers_file = f.readlines()
  47. for line in nameservers_file:
  48. if not line.startswith("#") and line.startswith("nameserver"):
  49. ns = line.split()[-1]
  50. default_nameservers.append(ns)
  51. #If we can't find default.resolv.conf, supply default Pardus DNS servers
  52. else:
  53. default_nameservers = ["193.140.100.210", "193.140.100.215", "193.140.100.220"]
  54. return default_nameservers
  55. class PardusNetworkProfile:
  56. """Represents COMAR based network profiles that we have been using in Pardus' network
  57. manager. We have two types of network profiles specified: wired or wireless
  58. """
  59. def __init__(self, name, connection_type, settings):
  60. self.profile_name = name
  61. self.connection_type = connection_type
  62. self.device = "none"
  63. self.net_mode = "none"
  64. self.name_mode = "none"
  65. self.state = "none"
  66. self.name_server = "none"
  67. self.net_address = "none"
  68. self.net_mask = "none"
  69. self.net_gateway = "none"
  70. self.remote = "none"
  71. self.auth = "none"
  72. self.auth_password = "none"
  73. self.set_attributes(settings)
  74. def set_attributes(self, settings):
  75. """We receive profile settings as dictionary type in 'settings'
  76. Map each key-value pairs for each object.
  77. """
  78. for key, value in settings.items():
  79. self.__dict__[key] = value
  80. def get_profile_name(self):
  81. return self.profile_name
  82. def get_connection_type(self):
  83. return self.connection_type
  84. def get_device(self):
  85. return self.device
  86. def get_net_mode(self):
  87. return self.net_mode
  88. def get_name_mode(self):
  89. return self.name_mode
  90. def get_state(self):
  91. return self.state
  92. def get_name_server(self):
  93. return self.name_server
  94. def get_net_address(self):
  95. return self.net_address
  96. def get_net_mask(self):
  97. return self.net_mask
  98. def get_net_gateway(self):
  99. return self.net_gateway
  100. def get_remote(self):
  101. return self.remote
  102. def get_auth(self):
  103. return self.auth
  104. def get_auth_password(self):
  105. return self.auth_password
  106. class NetworkManagerProfile:
  107. """Represents network profiles used in NetworkManager. In NetworkManager,
  108. settings are kept in a seperate file for each profile.
  109. """
  110. def __init__(self, name, pardus_profile):
  111. """Each section is kept in corresponding class attribute"""
  112. self.cfg = ConfigParser.ConfigParser()
  113. self.connection = Connection(pardus_profile)
  114. self.ipv4 = IpV4(pardus_profile)
  115. self.ipv6 = IpV6(pardus_profile)
  116. self._802_3_ethernet = self.set_802_3_ethernet(pardus_profile)
  117. self._802_11_wireless = self.set_802_11_wireless(pardus_profile)
  118. self._802_11_wireless_security = self.set_802_11_wireless_security(pardus_profile)
  119. self.create_config()
  120. def set_802_3_ethernet(self, pardus_profile):
  121. """If this is a wired (802-3-ethernet) profile, set _802_3_ethernet
  122. attribute.
  123. """
  124. if pardus_profile.connection_type == "802-3-ethernet":
  125. return _802_3_Ethernet(pardus_profile)
  126. else:
  127. return "none"
  128. def set_802_11_wireless(self, pardus_profile):
  129. """If this is a wireless (802-11-wirelesss) profile, set
  130. _802_11_wireless attribute."""
  131. if pardus_profile.connection_type == "802-11-wireless":
  132. return _802_11_Wireless(pardus_profile)
  133. else:
  134. return "none"
  135. def set_802_11_wireless_security(self, pardus_profile):
  136. """If the wireless connection has any security restrictions, create a
  137. 802-11-wireless-security section with corresponding values.
  138. """
  139. if pardus_profile.get_connection_type() == "802-11-wireless": #Check if it is a wlan profile
  140. if pardus_profile.get_auth() == "none":
  141. self._802_11_wireless.security = "none"
  142. return "none"
  143. else:
  144. self._802_11_wireless.security = "802-11-wireless-security"
  145. return _802_11_Wireless_Security(pardus_profile)
  146. else:
  147. return "none"
  148. def create_config(self):
  149. """Create sections and options in the prfoile's config file by calling
  150. each options corresponding method.
  151. """
  152. #FIXME: Try to do it over loops ie. self[attr].set_config()
  153. for attr, value in self.__dict__.items():
  154. if attr == "connection":
  155. self.connection.set_config(self.cfg)
  156. if attr == "ipv4":
  157. self.ipv4.set_config(self.cfg)
  158. if attr == "ipv6":
  159. self.ipv6.set_config(self.cfg)
  160. if attr == "_802_3_ethernet" and not value == "none":
  161. self._802_3_ethernet.set_config(self.cfg)
  162. if attr == "_802_11_wireless" and not value == "none":
  163. self._802_11_wireless.set_config(self.cfg)
  164. if attr == "_802_11_wireless_security" and not value == "none":
  165. self._802_11_wireless_security.set_config(self.cfg)
  166. def write_config(self):
  167. """Write settings to profile file"""
  168. #Before writing to file we must convert underscores to dashes, moreover _id must be written as id, and _type as type
  169. if not os.path.exists(NetworkManager_conf_dir):
  170. os.makedirs(NetworkManager_conf_dir, mode=0755)
  171. profile_path = os.path.join("%s" % NetworkManager_conf_dir, self.connection._id)
  172. with open(profile_path, "wb") as configfile:
  173. self.cfg.write(configfile)
  174. def set_ownership(self):
  175. """NetworkManager is smart enough to understand whether a configuration file is valid
  176. by checking its permissions"""
  177. os.chmod(os.path.join("%s" % NetworkManager_conf_dir, self.connection._id), 0600)
  178. class Connection:
  179. """Instances of Connection class represent essential properties of a network connection to identify itself."""
  180. def __init__(self, pardus_profile):
  181. self.name = "connection"
  182. self._id = pardus_profile.get_profile_name()
  183. self.uuid = self.set_uuid(device = pardus_profile.get_device())
  184. self._type = pardus_profile.get_connection_type()
  185. self.autoconnect = self.set_autoconnect(pardus_profile)
  186. self.timestamp = "none"
  187. self.read_only = "false"
  188. def set_autoconnect(self, profile):
  189. """Set profile activation at startup"""
  190. if str(profile.get_state()) == "down":
  191. return "false"
  192. else:
  193. return "none" #NM set this 'true' as default if we dont set anything
  194. def set_uuid(self, device):
  195. """Generate random type UUID"""
  196. import uuid
  197. return str(uuid.uuid4())
  198. def set_config(self, cfg):
  199. """One single config file will be used to write settings"""
  200. cfg.add_section(self.name)
  201. for attr, value in self.__dict__.items():
  202. if value not in ["false", "none"] and attr != "name":
  203. #Before creating config file _id must be id, and _type must be type
  204. if attr == "_id" or attr == "_type" : attr = attr.split("_")[-1]
  205. #There isnt any underscore in config options, replace them with dashes if found any
  206. attr = attr.replace("_", "-")
  207. cfg.set(self.name, attr, value)
  208. class IpV4:
  209. """Instances of IpV4 class represent IPv4 based properties of a connection."""
  210. def __init__(self, pardus_profile):
  211. self.name = "ipv4"
  212. self.dns_search = "none"
  213. self.routes = "none"
  214. self.ignore_auto_routes = "false"
  215. self.ignore_auto_dns = "false"
  216. self.dhcp_client_id = "none"
  217. self.dhcp_send_hostname = "false"
  218. self.dhcp_hostname = "none"
  219. self.never_default = "false"
  220. self.method = pardus_profile.get_net_mode() # auto or manual, same as in NM
  221. self.addresses1 = self.set_addresses(pardus_profile) # NM uses array of IPv4 address structures. We have only one for each iface
  222. self.dns = self.set_dns(pardus_profile)
  223. def set_dns(self, pardus_profile):
  224. """Decide whether to use default, custom or auto (DHCP assigned) nameservers"""
  225. if pardus_profile.get_name_mode() == "default":
  226. default_nameservers = ";".join( get_default_nameservers())
  227. default_nameservers = default_nameservers + ";" # Make sure addresses end with ';'
  228. self.ignore_auto_dns = "true"
  229. return str(default_nameservers)
  230. elif pardus_profile.get_name_mode() == "custom":
  231. name_server = str(pardus_profile.get_name_server())
  232. name_server = name_server + ";"
  233. self.ignore_auto_dns = "true"
  234. return str(name_server)
  235. else:
  236. # Nothing done in auto option
  237. return "none"
  238. def set_addresses(self, pardus_profile):
  239. """Set network addresses from given settings"""
  240. addresses = []
  241. if self.method == "manual":
  242. net_mask = self.calculate_prefix(pardus_profile.get_net_mask())
  243. addresses.append(str(pardus_profile.get_net_address()))
  244. addresses.append(str(net_mask))
  245. addresses.append(str(pardus_profile.get_net_gateway()))
  246. addresses = ";".join(addresses)
  247. addresses = addresses + ";" # Make sure addresses end with ';'
  248. return addresses
  249. else:
  250. return "none"
  251. def decimal2binary(self, n):
  252. """Convert decimal octet value to binary format"""
  253. octet = ["0", "0", "0", "0", "0", "0", "0", "0"]
  254. index = 0
  255. if n < 0 or n > 255:
  256. raise ValueError, "Octet value must be between [0-255]"
  257. if n == 0:
  258. return "".join(octet)
  259. while n > 0:
  260. octet[index] = str((n % 2))
  261. index += 1
  262. n = n >> 1
  263. octet.reverse()
  264. return "".join(octet)
  265. def calculate_prefix(self, net_mask):
  266. """Convert netmask value to CIDR prefix type which is between [1-32] as told in NM spec
  267. See http://mail.gnome.org/archives/networkmanager-list/2008-August/msg00076.html"""
  268. octets = net_mask.split(".")
  269. octet_in_binary = []
  270. netmask_value = 0
  271. for octet in octets:
  272. ret = self.decimal2binary(int(octet))
  273. octet_in_binary.append(ret)
  274. for i in "".join(octet_in_binary):
  275. if int(i) == 1 : netmask_value += 1
  276. return netmask_value
  277. def set_config(self, cfg):
  278. """One single config file will be used to write settings"""
  279. cfg.add_section(self.name)
  280. for attr, value in self.__dict__.items():
  281. if value not in ["false", "none"] and attr != "name":
  282. attr = attr.replace("_", "-")
  283. cfg.set(self.name, attr, value)
  284. class IpV6:
  285. """Instances of IpV6 class represent IPv6 based properties of a connection."""
  286. def __init__(self, pardus_profile):
  287. self.name = "ipv6"
  288. self.method = "none"
  289. self.dns = "none"
  290. self.dns_search = "none"
  291. self.addresses = "none"
  292. self.routes = "none"
  293. self.ignore_auto_routes = "false"
  294. self.ignore_auto_dns = "false"
  295. self.dhcp_client_id = "none"
  296. self.dhcp_send_hostname = "none"
  297. self.dhcp_hostname = "none"
  298. self.never_default = "false"
  299. self.set_method()
  300. def set_method(self):
  301. """Ignoring by default for nowadays"""
  302. self.method = "ignore"
  303. def set_config(self, cfg):
  304. """One single config file will be used to write settings"""
  305. cfg.add_section(self.name)
  306. for attr, value in self.__dict__.items():
  307. if value not in ["false", "none"] and attr != "name":
  308. attr = attr.replace("_", "-")
  309. cfg.set(self.name, attr, value)
  310. class _802_3_Ethernet:
  311. """Instances of _802_3_Ethernet class represent options related with ethernet device."""
  312. def __init__(self, pardus_profile):
  313. self.name = "802-3-ethernet"
  314. self.port = "none"
  315. self.speed = "none" #0
  316. self.duplex = "full"
  317. self.auto_negotiate = "false"
  318. self.mac_address = self.set_mac_address(pardus_profile.get_device())
  319. self.mtu = "none" #0
  320. def set_mac_address(self, iface):
  321. """Return MAC addresses of given interface on the machine"""
  322. if os.path.exists("/sys/class/net/%s" % iface):
  323. return open("/sys/class/net/%s/address" % iface).read().strip()
  324. return "none"
  325. def set_config(self, cfg):
  326. """One single config file will be used to write settings"""
  327. cfg.add_section(self.name)
  328. for attr, value in self.__dict__.items():
  329. if value not in ["false", "none"] and attr != "name":
  330. attr = attr.replace("_", "-")
  331. cfg.set(self.name, attr, value)
  332. class _802_11_Wireless:
  333. """Instances of _802_11_Wireless class represent options related with wireless device."""
  334. def __init__(self, pardus_profile):
  335. self.name = "802-11-wireless"
  336. self.ssid = self.set_ssid(pardus_profile)
  337. self.band = "none"
  338. self.channel = "0"
  339. self.bssid = "none"
  340. self.rate = "0"
  341. self.tx_power = "0"
  342. self.mtu = "0"
  343. self.seen_bssids = "none"
  344. self.security = "none"
  345. self.mode = self.set_mode(pardus_profile)
  346. self.mac_address = self.set_mac_address(pardus_profile.get_device())
  347. def set_mac_address(self, iface):
  348. """Return MAC addresses of given interface on the machine"""
  349. if os.path.exists("/sys/class/net/%s" % iface):
  350. return open("/sys/class/net/%s/address" % iface).read().strip()
  351. return "none"
  352. def set_mode(self, pardus_profile):
  353. """One of 'infrastructure' or 'adhoc'. If blank, infrastructure is assumed"""
  354. #TODO: How to determine mode (adhoc or infrastructure) from old profile settings
  355. return "infrastructure"
  356. def set_ssid(self, pardus_profile):
  357. """Set ssid value"""
  358. if pardus_profile.connection_type == "802-11-wireless":
  359. return str(pardus_profile.get_remote())
  360. else:
  361. return "none"
  362. def set_config(self, cfg):
  363. """One single config file will be used to write settings"""
  364. cfg.add_section(self.name)
  365. for attr, value in self.__dict__.items():
  366. if value not in ["false", "none", "0"] and attr != "name":
  367. attr = attr.replace("_", "-")
  368. cfg.set(self.name, attr, value)
  369. class _802_11_Wireless_Security:
  370. """Instances of _802_11_Wireless_Security class represent authentication properties and operations
  371. used in order to be included in networks requiring them.
  372. """
  373. def __init__(self, pardus_profile):
  374. self.name = "802-11-wireless-security"
  375. self.key_mgmt = "none"
  376. self.wep_tx_keyidx = "0"
  377. self.auth_alg = "none"
  378. self.proto = "none"
  379. self.pairwise = "none"
  380. self.group = "none"
  381. self.leap_username = "none"
  382. self.wep_key0 = "none"
  383. self.wep_key1 = "none"
  384. self.wep_key2 = "none"
  385. self.wep_key3 = "none"
  386. self.psk = "none"
  387. self.leap_password = "none"
  388. self.wep_key_type = "0"
  389. self.set_up_wireless_security(pardus_profile)
  390. def set_up_wireless_security(self, pardus_profile):
  391. """WEP, WPA or none type security based operations"""
  392. if pardus_profile.get_auth() in ["wep", "wepascii"]:
  393. self.set_wep(pardus_profile)
  394. elif pardus_profile.get_auth() == "wpa-psk":
  395. self.set_wpa(pardus_profile)
  396. else:
  397. return
  398. def set_wpa(self, pardus_profile):
  399. """Set up WPA based networks"""
  400. self.key_mgmt = "wpa-psk"
  401. self.psk = str(pardus_profile.get_auth_password())
  402. def set_wep(self, pardus_profile):
  403. """Set up WEP based networks"""
  404. self.auth_alg = "open" #TODO: or 'shared' ??
  405. self.key_mgmt = "None" # Which stands for WEP based key management
  406. self.wep_key0 = str(pardus_profile.get_auth_password()) # Default index
  407. self.wep_key_type = "1" # Interpret WEP keys as hex or ascii keys
  408. def set_config(self, cfg):
  409. """One single config file will be used to write settings"""
  410. cfg.add_section(self.name)
  411. for attr, value in self.__dict__.items():
  412. if value not in ["false", "none", "0"] and attr != "name":
  413. attr = attr.replace("_", "-")
  414. # key-mgmt=none is a mandatory assignment for WEP based
  415. # configurations. As we set all class members' default value
  416. # to 'none', and then filter to set config file with non-none
  417. # values, key-mgmt is set to 'None' intentionally to pass that
  418. # filtering, and its value is set later on by lowercasing to
  419. # hack around this situation.
  420. if attr == "key-mgmt":
  421. value = value.lower()
  422. cfg.set(self.name, attr, value)
  423. class Migrator:
  424. """Read COMAR based network profiles we have been using in Pardus' network manager and
  425. transform them into NetworkManager profile type.
  426. """
  427. def __init__(self):
  428. self.pardus_profiles = []
  429. self.network_manager_profiles = []
  430. self.lan_config_path = lan_config_file
  431. self.wlan_config_path = wlan_config_file
  432. self.read_pardus_profiles()
  433. def read_pardus_profiles(self):
  434. """Read wired/wireless profile settings, create PardusNetworkProfile
  435. object for each one, and store them in a list.
  436. """
  437. self.lan_config = ConfigParser.ConfigParser()
  438. self.lan_config.read(self.lan_config_path)
  439. connection_type = "802-3-ethernet"
  440. for section in self.lan_config.sections():
  441. lan_settings = {}
  442. for option in self.lan_config.options(section):
  443. if option == "device":
  444. #To strip device name from long device string
  445. lan_settings[option] = self.lan_config.get(section, option).split("_")[-1]
  446. else:
  447. lan_settings[option] = self.lan_config.get(section, option)
  448. p = PardusNetworkProfile(section, connection_type, lan_settings)
  449. self.pardus_profiles.append(p)
  450. self.wlan_config = ConfigParser.ConfigParser()
  451. self.wlan_config.read(self.wlan_config_path)
  452. connection_type = "802-11-wireless"
  453. for section in self.wlan_config.sections():
  454. wlan_settings = {}
  455. for option in self.wlan_config.options(section):
  456. if option == "device":
  457. wlan_settings[option] = self.wlan_config.get(section, option).split("_")[-1]
  458. else:
  459. wlan_settings[option] = self.wlan_config.get(section, option)
  460. p = PardusNetworkProfile(section, connection_type, wlan_settings)
  461. self.pardus_profiles.append(p)
  462. def transform_profiles(self):
  463. """Convert Pardus' network profiles to NetworkManager profiles"""
  464. if len(self.pardus_profiles) > 0: # Make sure we have some profiles to migrate
  465. for profile in self.pardus_profiles:
  466. network_manager_profile = NetworkManagerProfile(profile.get_profile_name, profile)
  467. self.network_manager_profiles.append(network_manager_profile)
  468. def write_network_manager_profiles(self):
  469. """Create profile file for each NetworkManager profile and change ownerships to 0600"""
  470. if len(self.network_manager_profiles) > 0: # Make sure we have some profiles to migrate
  471. for profile in self.network_manager_profiles:
  472. profile.write_config()
  473. profile.set_ownership()
  474. with open(migration_sign, "w") as f:
  475. pass
  476. if __name__ == "__main__":
  477. print "DEBUG: Checking whether network profiles migrated..."
  478. if not is_migrated_before():
  479. m = Migrator()
  480. m.transform_profiles()
  481. m.write_network_manager_profiles()
  482. print "DEBUG: Network profiles migration completed successfully!"
  483. else:
  484. print "DEBUG: Network profiles migration seems already done before. Nothing done this time!"