Moti Asayag has posted comments on this change.

Change subject: core: Adds custom properties to VmDevice
......................................................................


Patch Set 11: (27 inline comments)

Nice work Martin and thanks for the comprehensive test which settle the entire 
thing down.

I've raised concern about the non-deterministic order of the custom properties 
with the VmDevice class:

There is a chance a device will have properties: a=1,b=2 after saving it to the 
DB and reloading it will be b=2, a=1
Comparing the two objects will show they aren't equal despite they suppose to 
be the same.

As i see it, the correct way (IMO) is keeping the properties in map and not as 
a string.

I'd like to hear other opinions as well.

....................................................
File 
backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDeviceDAODbFacadeImpl.java
Line 49:                 .addValue("is_managed", entity.getIsManaged())
Line 50:                 .addValue("is_plugged", entity.getIsPlugged())
Line 51:                 .addValue("is_readonly", entity.getIsReadOnly())
Line 52:                 .addValue("alias", entity.getAlias())
Line 53:                 .addValue("custom_properties", 
entity.getCustomProperties());
please note that eventually, this is the only place where translation into 
String of the properties should happen. In all other places in the application, 
the custom properties should be managed as a Map<String, String> rather having 
the parsing logic duplicated in each component (UI, Rest and engine)
Line 54:     }
Line 55: 
Line 56:     @Override
Line 57:     protected RowMapper<VmDevice> createEntityRowMapper() {


Line 150:             vmDevice.setIsManaged(rs.getBoolean("is_managed"));
Line 151:             vmDevice.setIsPlugged(rs.getBoolean("is_plugged"));
Line 152:             vmDevice.setIsReadOnly(rs.getBoolean("is_readonly"));
Line 153:             vmDevice.setAlias(rs.getString("alias"));
Line 154:             
vmDevice.setCustomProperties(rs.getString("custom_properties"));
and here they should be translated into Map<String, String> from string.

But as said above, could be done in the future.
Line 155:             return vmDevice;
Line 156:         }
Line 157:     }
Line 158: 


....................................................
File backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties
Line 174: ACTION_TYPE_FAILED_MIGRATION_TO_SAME_HOST=Cannot ${action} ${type}. 
source and destination is the same.
Line 175: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_SYNTAX=Cannot 
${action} ${type} if custom properties are in invalid format. Please check the 
input.
Line 176: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_KEYS=Cannot 
${action} ${type} if some of the specified custom properties are not configured 
by the system. The keys are: ${MissingKeys}
Line 177: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_VALUES=Cannot 
${action} ${type} if some of the specified custom properties have illegal 
values. The keys are: ${WrongValueKeys}
Line 178: ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION=Cannot 
${action} ${type} custom properties are not supported in version: 
${NotSupportedInVersion}
please add a dot after ${type} and start custom with an upper case:
   ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION=Cannot 
${action} ${type}. Custom properties are not support
ed in version: ${NotSupportedInVersion}.
Line 179: ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES=Cannot 
${action} ${type} custom properties are not supported for device type: 
${InvalidDeviceType}
Line 180: ACTION_TYPE_FAILED_VDS_VM_CLUSTER=Cannot ${action} ${type}. There are 
no available running Hosts in the Host Cluster.
Line 181: ACTION_TYPE_FAILED_VDS_VM_MEMORY=Cannot ${action} ${type}. There are 
no available running Hosts with sufficient memory in VM's Cluster .
Line 182: ACTION_TYPE_FAILED_VDS_VM_CPUS=Cannot ${action} ${type}. There are no 
available running Hosts with enough cores in VM's Cluster .


Line 175: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_SYNTAX=Cannot 
${action} ${type} if custom properties are in invalid format. Please check the 
input.
Line 176: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_KEYS=Cannot 
${action} ${type} if some of the specified custom properties are not configured 
by the system. The keys are: ${MissingKeys}
Line 177: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_VALUES=Cannot 
${action} ${type} if some of the specified custom properties have illegal 
values. The keys are: ${WrongValueKeys}
Line 178: ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION=Cannot 
${action} ${type} custom properties are not supported in version: 
${NotSupportedInVersion}
Line 179: ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES=Cannot 
${action} ${type} custom properties are not supported for device type: 
${InvalidDeviceType}
same as above:

ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES=Cannot ${action} 
${type}. Custom properties are not supporte
d for device type: ${InvalidDeviceType}.
Line 180: ACTION_TYPE_FAILED_VDS_VM_CLUSTER=Cannot ${action} ${type}. There are 
no available running Hosts in the Host Cluster.
Line 181: ACTION_TYPE_FAILED_VDS_VM_MEMORY=Cannot ${action} ${type}. There are 
no available running Hosts with sufficient memory in VM's Cluster .
Line 182: ACTION_TYPE_FAILED_VDS_VM_CPUS=Cannot ${action} ${type}. There are no 
available running Hosts with enough cores in VM's Cluster .
Line 183: ACTION_TYPE_FAILED_VDS_VM_NETWORKS=Cannot ${action} ${type}. There 
are no available running Hosts with all the networks used by the VM.


....................................................
File 
backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/customprop/CustomPropertiesUtils.java
Line 115:         for (String property : propertiesStrs) {
Line 116: 
Line 117:             Pattern pattern = null;
Line 118:             String[] propertyParts = 
property.split(KEY_VALUE_DELIMETER, 2);
Line 119:             if (propertyParts.length == 1) { // there is no 
value(regex) for the property - we assume
please move this comment above the if statement as we don't have a convention 
of adding comment at the end of the code line
Line 120:                 // in that case that any value is allowed except for 
the properties delimeter
Line 121:                 pattern = VALUE_PATTERN;
Line 122:             } else {
Line 123:                 pattern = Pattern.compile(propertyParts[1]);


Line 167:             return Collections.emptyList();
Line 168:         }
Line 169:         // Syntax error is one of the most severe errors, and should 
be returned without checking the rest of the errors
Line 170:         if (errorsList.size() == 1 && errorsList.get(0).getReason() 
== ValidationFailureReason.SYNTAX_ERROR) {
Line 171:             return Arrays.asList(syntaxErrorMessage);
why not simplify it by squashing the two if statements into the same if 
statement in the following manner:

  if (errorsList.size() == 1 && 
isSevereErrorDetected(errorsList.get(0).getReason())) {
      return Arrays.asList(syntaxErrorMessage);
  }

   boolean isSevereErrorDetected(ValidationFailureReason failure) {
       return failure == ValidationFailureReason.SYNTAX_ERROR
           || failure == ValidationFailureReason.UNSUPPORTED_VERSION;
   }
Line 172:         }
Line 173:         // Unsupported version is one of the most severe errors, and 
should be returned without checking the rest of the
Line 174:         // errors
Line 175:         if (errorsList.size() == 1 && errorsList.get(0).getReason() 
== ValidationFailureReason.UNSUPPORTED_VERSION) {


....................................................
File 
backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/customprop/DevicePropertiesUtils.java
Line 113:         // init supported device types
Line 114:         supportedDeviceTypes = new ArrayList<>();
Line 115:         for (VmDeviceGeneralType t : VmDeviceGeneralType.values()) {
Line 116:             if (t != VmDeviceGeneralType.UNKNOWN) {
Line 117:                 // custom properties cannot be set for UNKNOWN
please move the comment line above the if statement
Line 118:                 supportedDeviceTypes.add(t);
Line 119:             }
Line 120:         }
Line 121: 


Line 206:                         deviceProperties.get(version).put(type, 
props);
Line 207:                     }
Line 208:                 }
Line 209:             }
Line 210:         } catch (Throwable ex) {
Would you mind replacing Throwable with Exception since it will treat Error 
exceptions on the same manner while it should be reflected beyond of the scope 
of this.
Line 211:             throw new InitializationException(ex);
Line 212:         }
Line 213:     }
Line 214: 


Line 231:         if (!FeatureSupported.deviceCustomProperties(version)) {
Line 232:             return Collections.emptySet();
Line 233:         }
Line 234: 
Line 235:         Set<VmDeviceGeneralType> types;
a matter of taste - types can be avoided for the sake of return statements from 
the if-else block.
Line 236:         EnumMap<VmDeviceGeneralType, Map<String, Pattern>> map = 
deviceProperties.get(version);
Line 237:         if (map.isEmpty()) {
Line 238:             // no device type has any properities
Line 239:             types = Collections.emptySet();


Line 308:      * @return map containing all device custom properties
Line 309:      */
Line 310:     public Map<String, String> convertProperties(Version version, 
VmDeviceGeneralType type, String properties) {
Line 311:         Map<String, String> map = new HashMap<String, String>();
Line 312:         if (!StringUtils.isEmpty(properties)) {
please use StringUtils.isNotEmpty instead negating the condition.
Line 313:             populateProperties(version, type, properties, map);
Line 314:         }
Line 315:         return map;
Line 316:     }


Line 354:         return sb.toString();
Line 355:     }
Line 356: 
Line 357:     /**
Line 358:      * Parses a string of device properties and stores them to a map 
of key,value
I think the end of this statement  "of key,value" is implied by noting it is a 
map
Line 359:      *
Line 360:      * @param version
Line 361:      *            version of the cluster that the VM containing the 
device is in
Line 362:      * @param type


Line 371:             VmDeviceGeneralType type,
Line 372:             String properties,
Line 373:             Map<String, String> propertiesMap) {
Line 374: 
Line 375:         List<ValidationError> results = new 
ArrayList<CustomPropertiesUtils.ValidationError>();
there are 3 levels of nested if. you can flatten it by early return, and avoid 
the redundant else block and the third level if:

  if (!FeatureSupported.deviceCustomProperties(version)) {
    results.add(unsupportedVersionValidationError);
    return results;
  }

  if (!supportedDeviceTypes.contains(type) || StringUtils.isEmpty(properties)) {
    return results;
  }

  Set<ValidationError> errorsSet = new 
HashSet<CustomPropertiesUtils.ValidationError>();
  String keyValuePairs[] = SEMICOLON_PATTERN.split(properties);
  ...
  results.addAll(errorsSet);
  return results;
}
Line 376:         if (FeatureSupported.deviceCustomProperties(version)) {
Line 377:             if (supportedDeviceTypes.contains(type)) {
Line 378:                 if (!StringUtils.isEmpty(properties)) {
Line 379:                     Set<ValidationError> errorsSet = new 
HashSet<CustomPropertiesUtils.ValidationError>();


Line 383:                         String[] pairParts = 
keyValuePairStr.split(KEY_VALUE_DELIMETER, 2);
Line 384:                         String key = pairParts[0];
Line 385:                         String value = pairParts[1];
Line 386:                         if (propertiesMap.containsKey(key)) {
Line 387:                             errorsSet.add(new 
ValidationError(ValidationFailureReason.DUPLICATE_KEY, key));
please use proper system logging than the standard output or delete this line 
completely.
Line 388:                             System.out.println("Duplicate key");
Line 389:                             continue;
Line 390:                         }
Line 391:                         if 
(!deviceProperties.get(version).get(type).containsKey(key)) {


Line 389:                             continue;
Line 390:                         }
Line 391:                         if 
(!deviceProperties.get(version).get(type).containsKey(key)) {
Line 392:                             errorsSet.add(new 
ValidationError(ValidationFailureReason.KEY_DOES_NOT_EXIST, key));
Line 393:                             System.out.println("Key not found");
same as above
Line 394:                             continue;
Line 395:                         }
Line 396: 
Line 397:                         if 
(!deviceProperties.get(version).get(type).get(key).matcher(value).matches()) {


Line 395:                         }
Line 396: 
Line 397:                         if 
(!deviceProperties.get(version).get(type).get(key).matcher(value).matches()) {
Line 398:                             errorsSet.add(new 
ValidationError(ValidationFailureReason.INCORRECT_VALUE, key));
Line 399:                             System.out.println("Incorrect value");
same as above
Line 400:                             continue;
Line 401:                         }
Line 402:                         propertiesMap.put(key, value);
Line 403:                     }


Line 442:         // 1. Check if the key exists in device properties key set
Line 443:         // 2. Check if the value of the key is valid
Line 444:         // In case either 1 or 2 fails, add an error to the errors 
list
Line 445:         Map<String, String> map = new HashMap<String, String>();
Line 446:         List<ValidationError> result = populateProperties(version, 
type, properties, map);
perhaps populateProperties could be divided into 2 methods: only for verifying 
the input and the second to store them ?
Line 447:         return result;
Line 448:     }
Line 449: 
Line 450:     /**


Line 443:         // 2. Check if the value of the key is valid
Line 444:         // In case either 1 or 2 fails, add an error to the errors 
list
Line 445:         Map<String, String> map = new HashMap<String, String>();
Line 446:         List<ValidationError> result = populateProperties(version, 
type, properties, map);
Line 447:         return result;
the result could be replaced with inline.
Line 448:     }
Line 449: 
Line 450:     /**
Line 451:      * Validates device custom properties definition


....................................................
File 
backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/ovf/OvfReader.java
Line 237:         } else {
Line 238:             vmDevice.setIsReadOnly(Boolean.FALSE);
Line 239:         }
Line 240:         if (node.SelectSingleNode(OvfProperties.VMD_CUSTOM_PROP, 
_xmlNS) != null
Line 241:                 && 
!StringUtils.isEmpty(node.SelectSingleNode(OvfProperties.VMD_CUSTOM_PROP, 
_xmlNS).InnerText)) {
You can use StringUtils.isNotEmpty instead negating StringUtils.isEmpty
Line 242:             
vmDevice.setCustomProperties(String.valueOf(node.SelectSingleNode(OvfProperties.VMD_CUSTOM_PROP,
 _xmlNS).InnerText));
Line 243:         } else {
Line 244:             vmDevice.setCustomProperties(null);
Line 245:         }


....................................................
File 
backend/manager/modules/utils/src/test/java/org/ovirt/engine/core/utils/customprop/DevicePropertiesUtilsTest.java
Line 72:                 .getSupportedClusterLevels();
Line 73:         try {
Line 74:             mockedUtils.init();
Line 75:         } catch (InitializationException e) {
Line 76:             e.printStackTrace();
please use a proper logger instead of standard output or remove this line 
completely
Line 77:         }
Line 78:         return mockedUtils;
Line 79:     }
Line 80: 


Line 86:         DevicePropertiesUtils utils = mockDevicePropertiesUtils();
Line 87:         List<ValidationError> errors =
Line 88:                 utils.validateProperties(Version.v3_2, 
VmDeviceGeneralType.DISK, "bootable=true");
Line 89: 
Line 90:         assertFalse(errors.isEmpty());
this two lines are repeated for each failed test, please extract them into a 
method which receives the failure reason and errors list, and use it in the 
negative tests where possible i.e. :

  private void validateFailure(List<ValidationError> errors, 
ValidationFailureReason reason) {
      assertFalse(errors.isEmpty());
      assertEquals(reason, errors.get(0).getReason());
  }
Line 91:         assertEquals(ValidationFailureReason.UNSUPPORTED_VERSION, 
errors.get(0).getReason());
Line 92:     }
Line 93: 
Line 94:     /**


Line 210: 
Line 211:     /**
Line 212:      * Tries to validate custom device properties specification with 
invalid syntax
Line 213:      */
Line 214:     @Test
suggestion: just for easing the reading, perhaps add the violation to the 
method name? 

e.g. customDevPropSpecWithInvalidSyntaxInvalidTypeKey
Line 215:     public void customDevPropSpecWithInvalidSyntax1() {
Line 216:         String customDevPropSpec = 
"{typ=disk;prop={bootable=^(true|false)$}}";
Line 217:         DevicePropertiesUtils utils = 
DevicePropertiesUtils.getInstance();
Line 218: 


Line 221: 
Line 222:     /**
Line 223:      * Tries to validate custom device properties specification with 
invalid syntax
Line 224:      */
Line 225:     @Test
e.g. customDevPropSpecWithInvalidSyntaxIncompleteBrackets
Line 226:     public void customDevPropSpecWithInvalidSyntax2() {
Line 227:         String customDevPropSpec = 
"{type=disk;prop={bootable=^(true|false)$}";
Line 228:         DevicePropertiesUtils utils = 
DevicePropertiesUtils.getInstance();
Line 229: 


Line 232: 
Line 233:     /**
Line 234:      * Tries to validate custom device properties specification with 
invalid syntax
Line 235:      */
Line 236:     @Test
e.g. customDevPropSpecWithInvalidSyntaxMissingSemicolons
Line 237:     public void customDevPropSpecWithInvalidSyntax3() {
Line 238:         String customDevPropSpec = 
"{type=disk,prop={bootable=^(true|false)$}}";
Line 239:         DevicePropertiesUtils utils = 
DevicePropertiesUtils.getInstance();
Line 240: 


Line 471:         map.put("hotplug", "0");
Line 472: 
Line 473:         String value = utils.convertProperties(Version.v3_3, 
VmDeviceGeneralType.INTERFACE, map);
Line 474: 
Line 475:         // order of properties in string is unknown due to unknown 
map key ordering
ain't this raise concern about the comparison of two VmDevice objects with the 
same entities but with custom properties as noted below will fail in compare ? 

I think the order of the properties should be deterministic to avoid it.
If we had the custom properties kept as Map<String, String>, we shouldn't be 
concern about it.
Line 476:         assertTrue("speed=1024;duplex=half".equals(value) || 
"duplex=half;speed=1024".equals(value));
Line 477:     }


....................................................
File 
frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java
Line 462: 
Line 463:     @DefaultStringValue("Cannot ${action} ${type} if some of the 
specified custom properties have illegal values. The keys are: 
${WrongValueKeys}")
Line 464:     String 
ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_VALUES();
Line 465: 
Line 466:     @DefaultStringValue("Cannot ${action} ${type} custom properties 
are not supported in version: ${NotSupportedInVersion}")
please see comments about the message in 
backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties
Line 467:     String 
ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION();
Line 468: 
Line 469:     @DefaultStringValue("Cannot ${action} ${type} custom properties 
are not supported for device type: ${InvalidDeviceType}")
Line 470:     String 
ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES();


....................................................
File 
frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties
Line 173: ACTION_TYPE_FAILED_MIGRATION_IN_PROGRESS=Cannot ${action} ${type}. VM 
migration is in progress
Line 174: ACTION_TYPE_FAILED_MIGRATION_TO_SAME_HOST=Cannot ${action} ${type}. 
source and destination is the same.
Line 175: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_SYNTAX=Cannot 
${action} ${type} if custom properties are in invalid format. Please check the 
input.
Line 176: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_KEYS=Cannot 
${action} ${type} if some of the specified custom properties are not configured 
by the system. The keys are: ${MissingKeys}
Line 177: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_VALUES=Cannot 
${action} ${type} if some of the specified custom properties have illegal 
values. The keys are: ${WrongValueKeys}
please see comments about the message in 
backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties
Line 178: ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION=Cannot 
${action} ${type} custom properties are not supported in version: 
${NotSupportedInVersion}
Line 179: ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES=Cannot 
${action} ${type} custom properties are not supported for device type: 
${InvalidDeviceType}
Line 180: ACTION_TYPE_FAILED_VDS_VM_CLUSTER=Cannot ${action} ${type}. There are 
no available running Hosts in the Host Cluster.
Line 181: ACTION_TYPE_FAILED_VDS_VM_MEMORY=Cannot ${action} ${type}. There are 
no available running Hosts with sufficient memory in VM's Cluster .


....................................................
File 
frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties
Line 170: ACTION_TYPE_FAILED_MIGRATION_TO_SAME_HOST=Cannot ${action} ${type}. 
source and destination is the same.
Line 171: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_SYNTAX=Cannot 
${action} ${type} if custom properties are in invalid format. Please check the 
input.
Line 172: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_KEYS=Cannot 
${action} ${type} if some of the specified custom properties are not configured 
by the system. The keys are: ${MissingKeys}
Line 173: ACTION_TYPE_FAILED_INVALID_CUSTOM_PROPERTIES_INVALID_VALUES=Cannot 
${action} ${type} if some of the specified custom properties have illegal 
values. The keys are: ${WrongValueKeys}
Line 174: ACTION_TYPE_FAILED_CUSTOM_PROPERTIES_NOT_SUPPORTED_IN_VERSION=Cannot 
${action} ${type} custom properties are not supported in version: 
${NotSupportedInVersion}
please see previous comment about the content of these messages.
Line 175: ACTION_TYPE_FAILED_INVALID_DEVICE_TYPE_FOR_CUSTOM_PROPERTIES=Cannot 
${action} ${type} custom properties are not supported for device type: 
${InvalidDeviceType}
Line 176: ACTION_TYPE_FAILED_VDS_VM_CLUSTER=Cannot ${action} ${type}. There are 
no available running Hosts in the Host Cluster.
Line 177: ACTION_TYPE_FAILED_VDS_VM_MEMORY=Cannot ${action} ${type}. There are 
no available running Hosts with sufficient memory in VM's Cluster .
Line 178: ACTION_TYPE_FAILED_VDS_VM_CPUS=Cannot ${action} ${type}. There are no 
available running Hosts with enough cores in VM's Cluster .


--
To view, visit http://gerrit.ovirt.org/14814
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: comment
Gerrit-Change-Id: I07daa5cccbf8e3cbd6b4191bbc8ebf0729d9d9a0
Gerrit-PatchSet: 11
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Martin Peřina <mper...@redhat.com>
Gerrit-Reviewer: Allon Mureinik <amure...@redhat.com>
Gerrit-Reviewer: Alona Kaplan <alkap...@redhat.com>
Gerrit-Reviewer: Martin Peřina <mper...@redhat.com>
Gerrit-Reviewer: Michal Skrivanek <michal.skriva...@redhat.com>
Gerrit-Reviewer: Moti Asayag <masa...@redhat.com>
Gerrit-Reviewer: Yair Zaslavsky <yzasl...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to