Merge branch 'master' of ssh://git.qubes-os.org/var/lib/qubes/git/smoku/qubes-manager
Conflicts: qubesmanager/main.py qubesmanager/ui_editfwrulesdlg.py qubesmanager/ui_newfwruledlg.py
This commit is contained in:
		
						commit
						e829891094
					
				| @ -7,48 +7,76 @@ | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>500</width> | ||||
|     <height>280</height> | ||||
|     <height>335</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Edit Firewall Rules</string> | ||||
|    <string>VM Firewall</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="policyAllowRadioButton"> | ||||
|      <property name="text"> | ||||
|       <string>Allow network access except...</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QRadioButton" name="policyDenyRadioButton"> | ||||
|      <property name="text"> | ||||
|       <string>Deny network access except...</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <property name="sizeConstraint"> | ||||
|       <enum>QLayout::SetMaximumSize</enum> | ||||
|      </property> | ||||
|      <item> | ||||
|       <widget class="QTreeView" name="rulesTreeView"> | ||||
|        <property name="rootIsDecorated"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|        <property name="uniformRowHeights"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|        <property name="itemsExpandable"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|        <property name="allColumnsShowFocus"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|        <property name="expandsOnDoubleClick"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|        <attribute name="headerDefaultSectionSize"> | ||||
|         <number>40</number> | ||||
|        </attribute> | ||||
|        <attribute name="headerStretchLastSection"> | ||||
|         <bool>false</bool> | ||||
|        </attribute> | ||||
|        <attribute name="headerDefaultSectionSize"> | ||||
|         <number>40</number> | ||||
|        </attribute> | ||||
|        <attribute name="headerStretchLastSection"> | ||||
|         <bool>false</bool> | ||||
|        </attribute> | ||||
|       </widget> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|        <item> | ||||
|         <widget class="QTreeView" name="rulesTreeView"> | ||||
|          <property name="rootIsDecorated"> | ||||
|           <bool>false</bool> | ||||
|          </property> | ||||
|          <property name="uniformRowHeights"> | ||||
|           <bool>false</bool> | ||||
|          </property> | ||||
|          <property name="itemsExpandable"> | ||||
|           <bool>false</bool> | ||||
|          </property> | ||||
|          <property name="allColumnsShowFocus"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|          <property name="expandsOnDoubleClick"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|          <attribute name="headerDefaultSectionSize"> | ||||
|           <number>40</number> | ||||
|          </attribute> | ||||
|          <attribute name="headerStretchLastSection"> | ||||
|           <bool>false</bool> | ||||
|          </attribute> | ||||
|          <attribute name="headerDefaultSectionSize"> | ||||
|           <number>40</number> | ||||
|          </attribute> | ||||
|          <attribute name="headerStretchLastSection"> | ||||
|           <bool>false</bool> | ||||
|          </attribute> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QCheckBox" name="dnsCheckBox"> | ||||
|          <property name="text"> | ||||
|           <string>Allow DNS queries</string> | ||||
|          </property> | ||||
|          <property name="checked"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </item> | ||||
|      <item> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout"> | ||||
| @ -103,10 +131,13 @@ | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <tabstops> | ||||
|   <tabstop>policyAllowRadioButton</tabstop> | ||||
|   <tabstop>policyDenyRadioButton</tabstop> | ||||
|   <tabstop>dnsCheckBox</tabstop> | ||||
|   <tabstop>rulesTreeView</tabstop> | ||||
|   <tabstop>newRuleButton</tabstop> | ||||
|   <tabstop>editRuleButton</tabstop> | ||||
|   <tabstop>deleteRuleButton</tabstop> | ||||
|   <tabstop>rulesTreeView</tabstop> | ||||
|   <tabstop>buttonBox</tabstop> | ||||
|  </tabstops> | ||||
|  <resources/> | ||||
| @ -118,8 +149,8 @@ | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|      <x>320</x> | ||||
|      <y>330</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
| @ -134,8 +165,8 @@ | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|      <x>252</x> | ||||
|      <y>330</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|  | ||||
							
								
								
									
										203
									
								
								newfwruledlg.ui
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								newfwruledlg.ui
									
									
									
									
									
								
							| @ -9,12 +9,12 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>311</width> | ||||
|     <height>202</height> | ||||
|     <width>381</width> | ||||
|     <height>121</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>New Firewall Rule</string> | ||||
|    <string>New Address</string> | ||||
|   </property> | ||||
|   <property name="modal"> | ||||
|    <bool>true</bool> | ||||
| @ -22,9 +22,9 @@ | ||||
|   <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>30</x> | ||||
|      <y>160</y> | ||||
|      <width>271</width> | ||||
|      <x>10</x> | ||||
|      <y>80</y> | ||||
|      <width>361</width> | ||||
|      <height>32</height> | ||||
|     </rect> | ||||
|    </property> | ||||
| @ -35,170 +35,59 @@ | ||||
|     <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|    </property> | ||||
|   </widget> | ||||
|   <widget class="QLabel" name="label"> | ||||
|   <widget class="QLabel" name="label_2"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>10</x> | ||||
|      <y>10</y> | ||||
|      <y>14</y> | ||||
|      <width>62</width> | ||||
|      <height>17</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Name</string> | ||||
|     <string>Address</string> | ||||
|    </property> | ||||
|   </widget> | ||||
|   <widget class="QGroupBox" name="groupBox"> | ||||
|   <widget class="QLineEdit" name="addressEdit"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>10</x> | ||||
|      <y>40</y> | ||||
|      <width>291</width> | ||||
|      <height>121</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="title"> | ||||
|     <string/> | ||||
|    </property> | ||||
|    <widget class="QLabel" name="label_2"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>10</x> | ||||
|       <y>10</y> | ||||
|       <width>62</width> | ||||
|       <height>17</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>Address</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QLabel" name="label_3"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>190</x> | ||||
|       <y>10</y> | ||||
|       <width>62</width> | ||||
|       <height>17</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>Netmask</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QCheckBox" name="allowCheckBox"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>200</x> | ||||
|       <y>80</y> | ||||
|       <width>71</width> | ||||
|       <height>23</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="sizePolicy"> | ||||
|      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||
|       <horstretch>0</horstretch> | ||||
|       <verstretch>0</verstretch> | ||||
|      </sizepolicy> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>Allow</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QLineEdit" name="addressEdit"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>10</x> | ||||
|       <y>30</y> | ||||
|       <width>171</width> | ||||
|       <height>27</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QLabel" name="label_4"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>10</x> | ||||
|       <y>62</y> | ||||
|       <width>31</width> | ||||
|       <height>21</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>Port</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QLabel" name="label_5"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>123</x> | ||||
|       <y>62</y> | ||||
|       <width>16</width> | ||||
|       <height>21</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>-</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QComboBox" name="netmaskComboBox"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>190</x> | ||||
|       <y>30</y> | ||||
|       <width>84</width> | ||||
|       <height>27</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QSpinBox" name="portBeginSpinBox"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>50</x> | ||||
|       <y>60</y> | ||||
|       <width>71</width> | ||||
|       <height>27</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="maximum"> | ||||
|      <number>65535</number> | ||||
|     </property> | ||||
|     <property name="value"> | ||||
|      <number>0</number> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QSpinBox" name="portEndSpinBox"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>130</x> | ||||
|       <y>60</y> | ||||
|       <width>71</width> | ||||
|       <height>27</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="maximum"> | ||||
|      <number>65535</number> | ||||
|     </property> | ||||
|    </widget> | ||||
|   </widget> | ||||
|   <widget class="QLineEdit" name="nameEdit"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>60</x> | ||||
|      <y>4</y> | ||||
|      <width>241</width> | ||||
|      <x>70</x> | ||||
|      <y>10</y> | ||||
|      <width>301</width> | ||||
|      <height>27</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|   </widget> | ||||
|   <widget class="QLabel" name="label_4"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>10</x> | ||||
|      <y>44</y> | ||||
|      <width>61</width> | ||||
|      <height>21</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Service</string> | ||||
|    </property> | ||||
|   </widget> | ||||
|   <widget class="QComboBox" name="serviceComboBox"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>70</x> | ||||
|      <y>40</y> | ||||
|      <width>301</width> | ||||
|      <height>27</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <property name="editable"> | ||||
|     <bool>true</bool> | ||||
|    </property> | ||||
|   </widget> | ||||
|  </widget> | ||||
|  <tabstops> | ||||
|   <tabstop>nameEdit</tabstop> | ||||
|   <tabstop>addressEdit</tabstop> | ||||
|   <tabstop>netmaskComboBox</tabstop> | ||||
|   <tabstop>portBeginSpinBox</tabstop> | ||||
|   <tabstop>portEndSpinBox</tabstop> | ||||
|   <tabstop>allowCheckBox</tabstop> | ||||
|   <tabstop>serviceComboBox</tabstop> | ||||
|   <tabstop>buttonBox</tabstop> | ||||
|  </tabstops> | ||||
|  <resources/> | ||||
| @ -210,12 +99,12 @@ | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>174</y> | ||||
|      <x>370</x> | ||||
|      <y>94</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>201</y> | ||||
|      <y>120</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
| @ -226,12 +115,12 @@ | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>300</x> | ||||
|      <y>180</y> | ||||
|      <x>370</x> | ||||
|      <y>100</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>201</y> | ||||
|      <y>120</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
| 
 | ||||
| import sys | ||||
| import os | ||||
| import re | ||||
| import xml.etree.ElementTree | ||||
| 
 | ||||
| from PyQt4.QtCore import * | ||||
| @ -28,10 +29,7 @@ from PyQt4.QtGui import * | ||||
| 
 | ||||
| from qubes.qubes import QubesVmCollection | ||||
| from qubes.qubes import QubesException | ||||
| from qubes.qubes import qubes_store_filename | ||||
| from qubes.qubes import QubesVmLabels | ||||
| from qubes.qubes import dry_run | ||||
| from qubes.qubes import qubes_guid_path | ||||
| 
 | ||||
| import ui_editfwrulesdlg | ||||
| import ui_newfwruledlg | ||||
| @ -41,70 +39,131 @@ class EditFwRulesDlg (QDialog, ui_editfwrulesdlg.Ui_EditFwRulesDlg): | ||||
|         super (EditFwRulesDlg, self).__init__(parent) | ||||
|         self.setupUi(self) | ||||
|         self.newRuleButton.clicked.connect(self.new_rule_button_pressed) | ||||
|         self.editRuleButton.clicked.connect(self.edit_rule_button_pressed) | ||||
|         self.deleteRuleButton.clicked.connect(self.delete_rule_button_pressed) | ||||
|         self.policyAllowRadioButton.toggled.connect(self.policy_radio_toggled) | ||||
|         self.dnsCheckBox.toggled.connect(self.dns_checkbox_toggled) | ||||
| 
 | ||||
|     def set_model(self, model): | ||||
|         self.__model = model | ||||
|         self.rulesTreeView.setModel(model) | ||||
|         self.rulesTreeView.header().setResizeMode(QHeaderView.ResizeToContents) | ||||
|         self.rulesTreeView.header().setResizeMode(0, QHeaderView.Stretch) | ||||
|         self.set_allow(model.allow) | ||||
|         self.dnsCheckBox.setChecked(model.allowDns) | ||||
|         self.setWindowTitle(model.get_vm_name() + " firewall") | ||||
| 
 | ||||
|     def set_allow(self, allow): | ||||
|         self.policyAllowRadioButton.setChecked(allow) | ||||
|         self.policyDenyRadioButton.setChecked(not allow) | ||||
| 
 | ||||
|     def policy_radio_toggled(self, on): | ||||
|         self.__model.allow = self.policyAllowRadioButton.isChecked() | ||||
| 
 | ||||
|     def dns_checkbox_toggled(self, on): | ||||
|         self.__model.allowDns = on | ||||
| 
 | ||||
|     def new_rule_button_pressed(self): | ||||
|         dialog = NewFwRuleDlg() | ||||
|         self.run_rule_dialog(dialog) | ||||
| 
 | ||||
|     def edit_rule_button_pressed(self): | ||||
|         dialog = NewFwRuleDlg() | ||||
|         dialog.set_ok_enabled(True) | ||||
|         row = self.rulesTreeView.selectedIndexes().pop().row() | ||||
|         item = self.__model.children[row] | ||||
|         dialog.addressEdit.setText(item.address) | ||||
|         service = self.__model.get_service_name(item.portBegin) | ||||
|         dialog.serviceComboBox.insertItem(0, service) | ||||
|         dialog.serviceComboBox.setCurrentIndex(0) | ||||
|         self.run_rule_dialog(dialog, row) | ||||
| 
 | ||||
|     def run_rule_dialog(self, dialog, row = None): | ||||
|         if dialog.exec_(): | ||||
|             name = dialog.nameEdit.text() | ||||
|             allow = dialog.allowCheckBox.isChecked() | ||||
|             address = dialog.addressEdit.text() | ||||
|             netmask = dialog.netmasks[dialog.netmaskComboBox.currentIndex()] | ||||
|             portBegin = dialog.portBeginSpinBox.value() | ||||
|             portEnd   = dialog.portEndSpinBox.value() | ||||
|             if portEnd <= portBegin: | ||||
|                 portEnd = None | ||||
|             service = dialog.serviceComboBox.currentText() | ||||
|             port = None | ||||
| 
 | ||||
|             if portBegin == 0 and portEnd is None: | ||||
|                 return | ||||
|             try: | ||||
|                 port = int(service) | ||||
|             except (TypeError, ValueError) as ex: | ||||
|                 port = self.__model.get_service_port(service) | ||||
| 
 | ||||
|             if name == "": | ||||
|                 QMessageBox.warning(None, "Incorrect name", "You need to name the rule.") | ||||
|                 return | ||||
| 
 | ||||
|             if address == "": | ||||
|                 QMessageBox.warning(None, "Incorrect address", "Pleas give an address for the rule.") | ||||
|                 return | ||||
| 
 | ||||
|             self.__model.appendChild(QubesFirewallRuleItem(name, allow, address, netmask, portBegin, portEnd)) | ||||
|             if port is not None: | ||||
|                 item = QubesFirewallRuleItem(address, 32, port, None) | ||||
|                 if row is not None: | ||||
|                     self.__model.setChild(row, item) | ||||
|                 else: | ||||
|                     self.__model.appendChild(item) | ||||
|             else: | ||||
|                 QMessageBox.warning(None, "Invalid service name", "Service '{0} is unknown.".format(service)) | ||||
| 
 | ||||
|     def delete_rule_button_pressed(self): | ||||
|         for i in set([index.row() for index in self.rulesTreeView.selectedIndexes()]): | ||||
|             self.__model.removeChild(i) | ||||
| 
 | ||||
| class QIPAddressValidator(QValidator): | ||||
|     def __init__(self, parent = None): | ||||
|         super (QIPAddressValidator, self).__init__(parent) | ||||
| 
 | ||||
|     def validate(self, input, pos): | ||||
|         hostname = input | ||||
| 
 | ||||
|         if len(hostname) > 255 or len(hostname) == 0: | ||||
|             return (QValidator.Intermediate, pos) | ||||
| 
 | ||||
|         if hostname[-1:] == ".": | ||||
|             hostname = hostname[:-1] | ||||
| 
 | ||||
|         if hostname[-1:] == "-": | ||||
|             return (QValidator.Intermediate, pos) | ||||
| 
 | ||||
|         allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) | ||||
|         if all(allowed.match(x) for x in hostname.split(".")): | ||||
|             return (QValidator.Acceptable, pos) | ||||
| 
 | ||||
|         return (QValidator.Invalid, pos) | ||||
| 
 | ||||
| class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg): | ||||
|     def __init__(self, parent = None): | ||||
|         super (NewFwRuleDlg, self).__init__(parent) | ||||
|         self.setupUi(self) | ||||
| 
 | ||||
|         self.netmasks = [ 32, 24, 16, 0 ] | ||||
|         for mask in self.netmasks: | ||||
|             self.netmaskComboBox.addItem(str(mask)) | ||||
|         self.set_ok_enabled(False) | ||||
|         self.addressEdit.setValidator(QIPAddressValidator()) | ||||
|         self.addressEdit.editingFinished.connect(self.address_editing_finished) | ||||
|         self.serviceComboBox.setValidator(QRegExpValidator(QRegExp("[a-z][a-z0-9-]+|[0-9]+", Qt.CaseInsensitive), None)) | ||||
| 
 | ||||
|         self.serviceComboBox.setInsertPolicy(QComboBox.InsertAtBottom) | ||||
|         self.populate_services_combo() | ||||
|         self.serviceComboBox.setInsertPolicy(QComboBox.InsertAtTop) | ||||
| 
 | ||||
|     def populate_services_combo(self): | ||||
|         displayed_services = [ | ||||
|                 'http', 'https', 'ftp', 'ftps', | ||||
|                 'smtp', 'pop3', 'pop3s', 'imap', 'imaps', 'nntp', 'nntps', | ||||
|                 'ssh', 'telnet', 'telnets', 'ntp', 'snmp', | ||||
|                 'ldap', 'ldaps', 'irc', 'ircs', 'xmpp-client', | ||||
|                 'syslog', 'printer', 'nfs', 'x11', | ||||
|             ] | ||||
|         for service in displayed_services: | ||||
|             self.serviceComboBox.addItem(service) | ||||
| 
 | ||||
|     def address_editing_finished(self): | ||||
|         self.set_ok_enabled(True) | ||||
| 
 | ||||
|     def set_ok_enabled(self, on): | ||||
|         ok_button = self.buttonBox.button(QDialogButtonBox.Ok) | ||||
|         if ok_button is not None: | ||||
|             ok_button.setEnabled(on) | ||||
| 
 | ||||
| class QubesFirewallRuleItem(object): | ||||
|     def __init__(self, name = str(), allow = bool(), address = str(), netmask = 32, portBegin = 0, portEnd = None): | ||||
|         self.__name = name | ||||
|         self.__allow = allow | ||||
|     def __init__(self, address = str(), netmask = 32, portBegin = 0, portEnd = None): | ||||
|         self.__address = address | ||||
|         self.__netmask = netmask | ||||
|         self.__portBegin = portBegin | ||||
|         self.__portEnd = portEnd | ||||
| 
 | ||||
|     @property | ||||
|     def name(self): | ||||
|         return self.__name | ||||
| 
 | ||||
|     @property | ||||
|     def allow(self): | ||||
|         return self.__allow | ||||
| 
 | ||||
|     @property | ||||
|     def address(self): | ||||
|         return self.__address | ||||
| @ -129,43 +188,63 @@ class QubesFirewallRulesModel(QAbstractItemModel): | ||||
|         QAbstractItemModel.__init__(self, parent) | ||||
| 
 | ||||
|         self.__columnValues = { | ||||
|                 0: lambda x: self.children[x].name, | ||||
|                 1: lambda x: self.children[x].address, | ||||
|                 2: lambda x: "/{0}".format(self.children[x].netmask), | ||||
|                 3: lambda x: "{0}-{1}".format(self.children[x].portBegin, self.children[x].portEnd) if self.children[x].portEnd is not None \ | ||||
|                         else self.children[x].portBegin, | ||||
|                 4: lambda x: "ALLOW" if self.children[x].allow else "DENY", | ||||
|                 0: lambda x: self.children[x].address, | ||||
|                 1: lambda x: "{0}-{1}".format(self.children[x].portBegin, self.children[x].portEnd) if self.children[x].portEnd is not None \ | ||||
|                         else self.get_service_name(self.children[x].portBegin), | ||||
|         } | ||||
|         self.__columnNames = { | ||||
|                 0: "Name", | ||||
|                 1: "Address", | ||||
|                 2: "Mask", | ||||
|                 3: "Port(s)", | ||||
|                 4: "Allow", | ||||
|                 0: "Address", | ||||
|                 1: "Service", | ||||
|         } | ||||
| 
 | ||||
|         self.__services = list() | ||||
|         pattern = re.compile("(?P<name>[a-z][a-z0-9-]+)\s+(?P<port>[0-9]+)/(?P<protocol>[a-z]+)", re.IGNORECASE) | ||||
|         f = open('/etc/services', 'r') | ||||
|         for line in f: | ||||
|             match = pattern.match(line) | ||||
|             if match is not None: | ||||
|                 service = match.groupdict() | ||||
|                 self.__services.append( (service["name"], int(service["port"]), service["protocol"]) ) | ||||
|         f.close() | ||||
| 
 | ||||
|     def get_service_name(self, port): | ||||
|         for service in self.__services: | ||||
|             if service[1] == port: | ||||
|                 return service[0] | ||||
|         return str(port) | ||||
| 
 | ||||
|     def get_service_port(self, name): | ||||
|         for service in self.__services: | ||||
|             if service[0] == name: | ||||
|                 return service[1] | ||||
|         return None | ||||
| 
 | ||||
|     def set_vm(self, vm): | ||||
|         self.__vm = vm | ||||
| 
 | ||||
|         self.clearChildren() | ||||
| 
 | ||||
|         conf = vm.get_firewall_conf() | ||||
| 
 | ||||
|         self.allow = conf["allow"] | ||||
|         self.allowDns = conf["allowDns"] | ||||
| 
 | ||||
|         for rule in conf["rules"]: | ||||
|             self.appendChild(QubesFirewallRuleItem( | ||||
|                 rule["name"], rule["allow"], rule["address"], | ||||
|                 rule["netmask"], rule["portBegin"], rule["portEnd"] | ||||
|                 rule["address"], rule["netmask"], rule["portBegin"], rule["portEnd"] | ||||
|                 )) | ||||
| 
 | ||||
|     def get_vm_name(self): | ||||
|         return self.__vm.name | ||||
| 
 | ||||
|     def apply_rules(self): | ||||
|         assert self.__vm is not None | ||||
| 
 | ||||
|         conf = { "allow": True, "rules": list() } | ||||
|         conf = { "allow": self.allow, "allowDns": self.allowDns, "rules": list() } | ||||
| 
 | ||||
|         for rule in self.children: | ||||
|             conf["rules"].append( | ||||
|                     { | ||||
|                         "allow": rule.allow, | ||||
|                         "name": rule.name, | ||||
|                         "address": rule.address, | ||||
|                         "netmask": rule.netmask, | ||||
|                         "portBegin": rule.portBegin, | ||||
| @ -241,6 +320,11 @@ class QubesFirewallRulesModel(QAbstractItemModel): | ||||
|         index = self.createIndex(i, 0) | ||||
|         self.dataChanged.emit(index, index) | ||||
| 
 | ||||
|     def setChild(self, i, child): | ||||
|         self.children[i] = child | ||||
|         index = self.createIndex(i, 0, child) | ||||
|         self.dataChanged.emit(index, index) | ||||
| 
 | ||||
|     def clearChildren(self): | ||||
|         self.__children = list() | ||||
| 
 | ||||
|  | ||||
| @ -50,7 +50,7 @@ class QubesConfigFileWatcher(ProcessEvent): | ||||
|     def __init__ (self, update_func): | ||||
|         self.update_func = update_func | ||||
|         pass | ||||
|      | ||||
| 
 | ||||
|     def process_IN_CLOSE_WRITE (self, event): | ||||
|         self.update_func() | ||||
| 
 | ||||
| @ -151,6 +151,50 @@ class VmInfoWidget (QWidget): | ||||
|     def update_vm_state (self, vm): | ||||
|         self.vm_icon.update() | ||||
| 
 | ||||
| class VmUsageWidget (QWidget): | ||||
|     def __init__(self, vm, parent = None): | ||||
|         super (VmUsageWidget, self).__init__(parent) | ||||
| 
 | ||||
|         self.cpu_widget = QProgressBar() | ||||
|         self.mem_widget = QProgressBar() | ||||
|         self.cpu_widget.setMinimum(0) | ||||
|         self.cpu_widget.setMaximum(100) | ||||
|         self.mem_widget.setMinimum(0) | ||||
|         self.mem_widget.setMaximum(100) | ||||
|         self.cpu_label = QLabel("CPU") | ||||
|         self.mem_label = QLabel("MEM") | ||||
| 
 | ||||
|         layout_cpu = QHBoxLayout() | ||||
|         layout_cpu.addWidget(self.cpu_label) | ||||
|         layout_cpu.addWidget(self.cpu_widget) | ||||
| 
 | ||||
|         layout_mem = QHBoxLayout() | ||||
|         layout_mem.addWidget(self.mem_label) | ||||
|         layout_mem.addWidget(self.mem_widget) | ||||
| 
 | ||||
|         layout = QVBoxLayout() | ||||
|         layout.addLayout(layout_cpu) | ||||
|         layout.addLayout(layout_mem) | ||||
| 
 | ||||
|         self.setLayout(layout) | ||||
| 
 | ||||
|         self.update_load(vm) | ||||
| 
 | ||||
|     def update_load(self, vm): | ||||
|         self.cpu_load = vm.get_cpu_total_load() if vm.is_running() else 0 | ||||
|         assert self.cpu_load >= 0 and self.cpu_load <= 100, "load = {0}".format(self.load) | ||||
|         self.mem_load = vm.get_mem()*100/qubes_host.memory_total if vm.is_running() else 0 | ||||
|         assert self.mem_load >= 0 and self.mem_load <= 100, "mem = {0}".format(self.load) | ||||
| 
 | ||||
|         self.cpu_widget.setValue(self.cpu_load) | ||||
|         self.mem_widget.setValue(self.mem_load) | ||||
| 
 | ||||
|     def resizeEvent(self, Event = None): | ||||
|         label_width = max(self.mem_label.width(), self.cpu_label.width()) | ||||
|         self.mem_label.setMinimumWidth(label_width) | ||||
|         self.cpu_label.setMinimumWidth(label_width) | ||||
|         super (VmUsageWidget, self).resizeEvent(Event) | ||||
| 
 | ||||
| class LoadChartWidget (QWidget): | ||||
| 
 | ||||
|     def __init__(self, vm, parent = None): | ||||
| @ -169,7 +213,7 @@ class LoadChartWidget (QWidget): | ||||
|         p = QPainter (self) | ||||
|         dx = 4 | ||||
| 
 | ||||
|         W = self.width()  | ||||
|         W = self.width() | ||||
|         H = self.height() - 5 | ||||
|         N = len(self.load_history) | ||||
|         if N > W/dx: | ||||
| @ -208,7 +252,7 @@ class MemChartWidget (QWidget): | ||||
|         p = QPainter (self) | ||||
|         dx = 4 | ||||
| 
 | ||||
|         W = self.width()  | ||||
|         W = self.width() | ||||
|         H = self.height() - 5 | ||||
|         N = len(self.load_history) | ||||
|         if N > W/dx: | ||||
| @ -241,16 +285,20 @@ class VmRowInTable(object): | ||||
|         self.info_widget = VmInfoWidget(vm) | ||||
|         table.setCellWidget(row_no, 0, self.info_widget) | ||||
| 
 | ||||
|         self.usage_widget = VmUsageWidget(vm) | ||||
|         table.setCellWidget(row_no, 1, self.usage_widget) | ||||
| 
 | ||||
|         self.load_widget = LoadChartWidget(vm) | ||||
|         table.setCellWidget(row_no, 1, self.load_widget) | ||||
|         table.setCellWidget(row_no, 2, self.load_widget) | ||||
| 
 | ||||
|         self.mem_widget = MemChartWidget(vm) | ||||
|         table.setCellWidget(row_no, 2, self.mem_widget) | ||||
|         table.setCellWidget(row_no, 3, self.mem_widget) | ||||
| 
 | ||||
| 
 | ||||
|     def update(self, counter): | ||||
|         self.info_widget.update_vm_state(self.vm) | ||||
|         if counter % 3 == 0: | ||||
|             self.usage_widget.update_load(self.vm) | ||||
|             self.load_widget.update_load(self.vm) | ||||
|             self.mem_widget.update_load(self.vm) | ||||
| 
 | ||||
| @ -270,7 +318,7 @@ class VmShutdownMonitor(QObject): | ||||
|         if not vm.is_running(): | ||||
|             return | ||||
| 
 | ||||
|         reply = QMessageBox.question(None, "VM Shutdown",  | ||||
|         reply = QMessageBox.question(None, "VM Shutdown", | ||||
|                                      "The VM <b>'{0}'</b> hasn't shutdown within the last {1} seconds, do you want to kill it?<br>".format(vm.name, vm_shutdown_timeout/1000), | ||||
|                                      "Kill it!", "Wait another {0} seconds...".format(vm_shutdown_timeout/1000)) | ||||
| 
 | ||||
| @ -298,11 +346,12 @@ class ThreadMonitor(QObject): | ||||
| 
 | ||||
| 
 | ||||
| class VmManagerWindow(QMainWindow): | ||||
|     columns_widths = [200, 150, 150] | ||||
|     columns_widths = [200, 200, 150, 150] | ||||
|     row_height = 50 | ||||
|     max_visible_rows = 14 | ||||
|     update_interval = 1000 # in msec | ||||
|     show_inactive_vms = True | ||||
|     columns_states = { 0: [0, 1], 1: [0, 2, 3] } | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         super(VmManagerWindow, self).__init__(parent) | ||||
| @ -342,8 +391,6 @@ class VmManagerWindow(QMainWindow): | ||||
|         self.action_shutdownvm.setDisabled(True) | ||||
|         self.action_updatevm.setDisabled(True) | ||||
| 
 | ||||
|         self.action_showcpuload.setDisabled(True) | ||||
| 
 | ||||
|         self.toolbar = self.addToolBar ("Toolbar") | ||||
|         self.toolbar.setFloatable(False) | ||||
|         self.addActions (self.toolbar, (self.action_createvm, self.action_removevm, | ||||
| @ -353,7 +400,7 @@ class VmManagerWindow(QMainWindow): | ||||
|                                    None, | ||||
|                                    self.action_showcpuload, | ||||
|                                    )) | ||||
|          | ||||
| 
 | ||||
|         self.table = QTableWidget() | ||||
|         self.setCentralWidget(self.table) | ||||
|         self.table.clear() | ||||
| @ -361,26 +408,27 @@ class VmManagerWindow(QMainWindow): | ||||
|         for (col, width) in enumerate (VmManagerWindow.columns_widths): | ||||
|             self.table.setColumnWidth (col, width) | ||||
| 
 | ||||
|         self.table.horizontalHeader().setStretchLastSection(True) | ||||
|         self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch) | ||||
|         self.table.horizontalHeader().setResizeMode(0, QHeaderView.Fixed) | ||||
|         self.table.setAlternatingRowColors(True) | ||||
|         self.table.verticalHeader().hide() | ||||
|         self.table.horizontalHeader().hide() | ||||
|         #self.table.setHorizontalHeaderLabels (["VM name & state", "CPU Load", "Memory Use"]) | ||||
|         self.table.setGridStyle(Qt.NoPen) | ||||
|         self.table.setSortingEnabled(False) | ||||
|         self.table.setSelectionBehavior(QTableWidget.SelectRows) | ||||
|         self.table.setSelectionMode(QTableWidget.SingleSelection) | ||||
|   | ||||
| 
 | ||||
|         self.__cpugraphs = self.action_showcpuload.isChecked() | ||||
|         self.update_table_columns() | ||||
| 
 | ||||
|         self.qvm_collection = QubesVmCollection() | ||||
|         self.setWindowTitle("Qubes VM Manager") | ||||
|   | ||||
| 
 | ||||
|         self.connect(self.table, SIGNAL("itemSelectionChanged()"), self.table_selection_changed) | ||||
| 
 | ||||
|         self.fill_table() | ||||
| 
 | ||||
|         tbl_W = 0 | ||||
|         for (i, w) in enumerate(VmManagerWindow.columns_widths): | ||||
|             tbl_W += w | ||||
|         tbl_W = self.get_minimum_table_width() | ||||
| 
 | ||||
|         # TODO: '6' -- WTF?! | ||||
|         tbl_H = self.toolbar.height() + 6 + \ | ||||
| @ -392,7 +440,6 @@ class VmManagerWindow(QMainWindow): | ||||
|         for i in range (0, n): | ||||
|             tbl_H += self.table.rowHeight(i) | ||||
| 
 | ||||
|         self.setMinimumWidth(tbl_W) | ||||
|         self.setGeometry(self.x(), self.y(), self.x() + tbl_W, self.y() + tbl_H) | ||||
| 
 | ||||
|         self.counter = 0 | ||||
| @ -489,6 +536,15 @@ class VmManagerWindow(QMainWindow): | ||||
|             self.counter += 1 | ||||
|             QTimer.singleShot (self.update_interval, self.update_table) | ||||
| 
 | ||||
|     def update_table_columns(self): | ||||
|         state = 1 if self.__cpugraphs else 0 | ||||
|         columns = self.columns_states[state] | ||||
| 
 | ||||
|         for i in range(0, self.table.columnCount()): | ||||
|             enabled = columns.count(i) > 0 | ||||
|             self.table.setColumnHidden(i, not enabled) | ||||
| 
 | ||||
|         self.setMinimumWidth(self.get_minimum_table_width()) | ||||
| 
 | ||||
|     def table_selection_changed (self): | ||||
|         vm = self.get_selected_vm() | ||||
| @ -502,6 +558,14 @@ class VmManagerWindow(QMainWindow): | ||||
|         self.action_updatevm.setEnabled(vm.is_updateable() and not vm.is_running()) | ||||
|         self.action_editfwrules.setEnabled(vm.is_networked() and (vm.is_appvm() or vm.is_disposablevm())) | ||||
| 
 | ||||
|     def get_minimum_table_width(self): | ||||
|         tbl_W = 0 | ||||
|         for (col, w) in enumerate(VmManagerWindow.columns_widths): | ||||
|             if not self.table.isColumnHidden(col): | ||||
|                 tbl_W += w | ||||
| 
 | ||||
|         return tbl_W | ||||
| 
 | ||||
|     def closeEvent (self, event): | ||||
|         self.hide() | ||||
|         event.ignore() | ||||
| @ -556,7 +620,7 @@ class VmManagerWindow(QMainWindow): | ||||
|             progress.setCancelButton(None) | ||||
|             progress.setModal(True) | ||||
|             progress.show() | ||||
|              | ||||
| 
 | ||||
|             while not thread_monitor.is_finished(): | ||||
|                 app.processEvents() | ||||
|                 time.sleep (0.1) | ||||
| @ -607,14 +671,14 @@ class VmManagerWindow(QMainWindow): | ||||
|         if vm.is_template(): | ||||
|             dependent_vms = self.qvm_collection.get_vms_based_on(vm.qid) | ||||
|             if len(dependent_vms) > 0: | ||||
|                 QMessageBox.warning (None, "Warning!",  | ||||
|                 QMessageBox.warning (None, "Warning!", | ||||
|                                      "This Template VM cannot be removed, because there is at least one AppVM that is based on it.<br>" | ||||
|                                      "<small>If you want to remove this Template VM and all the AppVMs based on it," | ||||
|                                      "you should first remove each individual AppVM that uses this template.</small>") | ||||
| 
 | ||||
|                 return | ||||
| 
 | ||||
|         reply = QMessageBox.question(None, "VM Removal Confirmation",  | ||||
|         reply = QMessageBox.question(None, "VM Removal Confirmation", | ||||
|                                      "Are you sure you want to remove the VM <b>'{0}'</b>?<br>" | ||||
|                                      "<small>All data on this VM's private storage will be lost!</small>".format(vm.name), | ||||
|                                      QMessageBox.Yes | QMessageBox.Cancel) | ||||
| @ -631,7 +695,7 @@ class VmManagerWindow(QMainWindow): | ||||
|             progress.setCancelButton(None) | ||||
|             progress.setModal(True) | ||||
|             progress.show() | ||||
|              | ||||
| 
 | ||||
|             while not thread_monitor.is_finished(): | ||||
|                 app.processEvents() | ||||
|                 time.sleep (0.1) | ||||
| @ -641,7 +705,7 @@ class VmManagerWindow(QMainWindow): | ||||
|             if thread_monitor.success: | ||||
|                 trayIcon.showMessage ("Qubes Manager", "VM '{0}' has been removed.".format(vm.name), msecs=3000) | ||||
|             else: | ||||
|                 QMessageBox.warning (None, "Error removing M!", "ERROR: {0}".format(thread_monitor.error_msg)) | ||||
|                 QMessageBox.warning (None, "Error removing VM!", "ERROR: {0}".format(thread_monitor.error_msg)) | ||||
| 
 | ||||
|     def do_remove_vm (self, vm, thread_monitor): | ||||
|         try: | ||||
| @ -694,7 +758,7 @@ class VmManagerWindow(QMainWindow): | ||||
|         vm = self.get_selected_vm() | ||||
|         assert vm.is_running() | ||||
| 
 | ||||
|         reply = QMessageBox.question(None, "VM Shutdown Confirmation",  | ||||
|         reply = QMessageBox.question(None, "VM Shutdown Confirmation", | ||||
|                                      "Are you sure you want to power down the VM <b>'{0}'</b>?<br>" | ||||
|                                      "<small>This will shutdown all the running applications within this VM.</small>".format(vm.name), | ||||
|                                      QMessageBox.Yes | QMessageBox.Cancel) | ||||
| @ -722,7 +786,8 @@ class VmManagerWindow(QMainWindow): | ||||
|             return | ||||
| 
 | ||||
|     def showcpuload(self): | ||||
|         pass | ||||
|         self.__cpugraphs = self.action_showcpuload.isChecked() | ||||
|         self.update_table_columns() | ||||
| 
 | ||||
|     def edit_fw_rules(self): | ||||
|         vm = self.get_selected_vm() | ||||
| @ -733,7 +798,33 @@ class VmManagerWindow(QMainWindow): | ||||
| 
 | ||||
|         if dialog.exec_(): | ||||
|             model.apply_rules() | ||||
|                     | ||||
|             QTimer.singleShot(5000, self.check_apply_fw_rules) | ||||
| 
 | ||||
|     def check_apply_fw_rules(self): | ||||
|         qvm_collection = QubesVmCollection() | ||||
|         qvm_collection.lock_db_for_reading() | ||||
|         qvm_collection.load() | ||||
|         qvm_collection.unlock_db() | ||||
| 
 | ||||
|         for vm in qvm_collection.values(): | ||||
|             if vm.is_fwvm(): | ||||
|                 error_file = "/local/domain/{0}/qubes_iptables_error".format(vm.get_xid()) | ||||
| 
 | ||||
|                 error = subprocess.Popen( | ||||
|                         ["/usr/bin/xenstore-read", error_file], | ||||
|                         stdout=subprocess.PIPE).communicate()[0] | ||||
|                 if error != "": | ||||
|                     vm.rules_applied = False | ||||
|                     trayIcon.showMessage ( | ||||
|                             "Error applying firewall rules on '{0}'!".format(vm.name), | ||||
|                             "ERROR: {0}".format(error.decode('string_escape')), | ||||
|                             QSystemTrayIcon.Critical | ||||
|                         ) | ||||
|                     retcode = subprocess.check_call ( | ||||
|                             ["/usr/bin/xenstore-write", error_file, ""]) | ||||
|                 else: | ||||
|                     vm.rules_applied = True | ||||
| 
 | ||||
| class QubesTrayIcon(QSystemTrayIcon): | ||||
|     def __init__(self, icon): | ||||
|         QSystemTrayIcon.__init__(self, icon) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski
						Marek Marczykowski