You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by vb...@apache.org on 2018/01/25 01:04:48 UTC

[ambari] branch branch-feature-AMBARI-14714 updated: [AMBARI-22817] Update backend code to handle new versioning schema. (#155)

This is an automated email from the ASF dual-hosted git repository.

vbrodetskyi pushed a commit to branch branch-feature-AMBARI-14714
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714 by this push:
     new 80d840e  [AMBARI-22817] Update backend code to handle new versioning schema. (#155)
80d840e is described below

commit 80d840e5d50614a0f148a650c9bfc7d21363a218
Author: vbrodetskyi <vb...@cybervisiontech.com>
AuthorDate: Thu Jan 25 03:04:45 2018 +0200

    [AMBARI-22817] Update backend code to handle new versioning schema. (#155)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
    
    * AMBARI-22817. Update backend code to handle new versioning schema.(vbrodetskyi)
---
 .../libraries/functions/module_version.py          | 158 ++++++++++++++++
 .../libraries/functions/mpack_version.py           | 207 +++++++++++++++++++++
 .../apache/ambari/server/utils/ModuleVersion.java  | 162 ++++++++++++++++
 .../apache/ambari/server/utils/MpackVersion.java   | 197 ++++++++++++++++++++
 .../ambari/server/utils/TestVersionUtils.java      | 142 ++++++++++++++
 ambari-server/src/test/python/TestVersion.py       | 100 +++++++++-
 6 files changed, 964 insertions(+), 2 deletions(-)

diff --git a/ambari-common/src/main/python/resource_management/libraries/functions/module_version.py b/ambari-common/src/main/python/resource_management/libraries/functions/module_version.py
new file mode 100644
index 0000000..6ec4ba3
--- /dev/null
+++ b/ambari-common/src/main/python/resource_management/libraries/functions/module_version.py
@@ -0,0 +1,158 @@
+"""
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+"""
+
+import re
+
+"""
+ This class should be used to compare module(service) versions.
+ Base method which should be used is parse(..), This method will validate and parse
+ version which you will pass as parameter, and return object of current class with
+ parsed version. Same thing you should do with another version, with which you are
+ planning to compare previous one. After that, use "==", "<", ">" to get final result.
+"""
+class ModuleVersion(object):
+  __module_version_pattern = "(?P<aMajor>[0-9]+).(?P<aMinor>[0-9]+).(?P<iMinor>[0-9]+).(?P<iMaint>[0-9]+)(-h(?P<hotfix>[0-9]+))*-b(?P<build>[0-9]+)"
+  __module_version_regex = re.compile(__module_version_pattern)
+
+  def __init__(self, apache_major, apache_minor, internal_minor, internal_maint, hotfix, build):
+    """
+    :type apache_major int
+    :type apache_minor int
+    :type internal_maint int
+    :type internal_minor int
+    :type hotfix int
+    :type build int
+    """
+    self.__apache_major = int(apache_major)
+    self.__apache_minor = int(apache_minor)
+    self.__internal_maint = int(internal_maint)
+    self.__internal_minor = int(internal_minor)
+    self.__hotfix = int(hotfix) if hotfix else 0  # hotfix is optional group
+    self.__build = int(build)
+
+  def __repr__(self):
+    return "{0}.{1}.{2}.{3}-h{4}-b{5}".format(*self.to_list())
+
+  def to_list(self):
+    """
+    Return version elements as list
+
+    :rtype list
+    """
+    return [
+      self.__apache_major,
+      self.__apache_minor,
+      self.__internal_minor,
+      self.__internal_maint,
+      self.__hotfix,
+      self.__build
+    ]
+
+  def __cmp__(self, other):
+    """
+    :type other ModuleVersion
+
+    :raise TypeError
+    """
+    if other and not isinstance(other, self.__class__):
+      raise TypeError("Operand type is different from {0}".format(self.__class__.__name__))
+
+    r = 0
+    x = self.to_list()
+    y = other.to_list()
+
+    for i in range(0, len(x)):
+      r = x[i] - y[i]
+      if r != 0:
+        break
+
+    return 1 if r > 0 else -1 if r < 0 else 0
+
+  @classmethod
+  def parse(cls, module_version):
+    """
+      Parse string to module version
+
+      :type module_version str
+      :rtype ModuleVersion
+      """
+    matcher = cls.validate(module_version)
+    return ModuleVersion(
+      matcher.group("aMajor"),
+      matcher.group("aMinor"),
+      matcher.group("iMinor"),
+      matcher.group("iMaint"),
+      matcher.group("hotfix"),
+      matcher.group("build")
+    )
+
+  @classmethod
+  def validate(cls, module_version):
+    """
+    Check if provided version is valid. If version is valid will return match object
+    or will raise exception.
+
+    :param module_version version to check
+    :type module_version str
+
+    :rtype __Match[T] | None
+
+    :raise ValueError
+    """
+
+    if not module_version:
+      raise ValueError("Module version can't be empty or null")
+
+    version = module_version.strip()
+
+    if not version:
+      raise ValueError("Module version can't be empty or null")
+
+    matcher = cls.__module_version_regex.match(version)
+
+    if not matcher:
+      raise ValueError("{0} is not a valid {1}".format(version, cls.__name__))
+
+    return matcher
+
+  @property
+  def apache_major(self):
+    return self.__apache_major
+
+  @property
+  def apache_minor(self):
+    return self.__apache_minor
+
+  @property
+  def internal_minor(self):
+    return self.__internal_minor
+
+  @property
+  def internal_maint(self):
+    return self.__internal_maint
+
+  @property
+  def hotfix(self):
+    return self.__hotfix
+
+  @property
+  def build(self):
+    return self.__build
+
+
+
diff --git a/ambari-common/src/main/python/resource_management/libraries/functions/mpack_version.py b/ambari-common/src/main/python/resource_management/libraries/functions/mpack_version.py
new file mode 100644
index 0000000..664f9fe
--- /dev/null
+++ b/ambari-common/src/main/python/resource_management/libraries/functions/mpack_version.py
@@ -0,0 +1,207 @@
+"""
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+"""
+
+import re
+
+"""
+ This class should be used to compare mpack and stack versions.
+ Base method which should be used is parse/parse_stack_version, depends
+ on which versions you want to compare. This method will validate and parse
+ version which you will pass as parameter, and return object of current class with
+ parsed version. Same thing you should do with another version, with which you are
+ planning to compare previous one. After that, use "==", ">", "<" to get final result.
+"""
+class MpackVersion(object):
+  __mpack_version_pattern = "(?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<maint>[0-9]+)(-h(?P<hotfix>[0-9]+))*-b(?P<build>[0-9]+)"
+  __mpack_legacy_stack_version_pattern = "(?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<maint>[0-9]+).(?P<hotfix>[0-9]+)(-(?P<build>[0-9]+))"
+  __mpack_version_regex = re.compile(__mpack_version_pattern)
+  __mpack_legacy_stack_version_regex = re.compile(__mpack_legacy_stack_version_pattern)
+
+  def __init__(self, major, minor, maint, hotfix, build):
+    """
+    :type major int
+    :type minor int
+    :type maint int
+    :type hotfix int
+    :type build int
+    """
+    self.__major = int(major)
+    self.__minor = int(minor)
+    self.__maint = int(maint)
+    self.__hotfix = int(hotfix) if hotfix else 0  # hotfix is optional group
+    self.__build = int(build)
+
+  def __repr__(self):
+    return "{0}.{1}.{2}-h{3}-b{4}".format(*self.to_list())
+
+  def to_list(self):
+    """
+    Return version elements as list
+
+    :rtype list
+    """
+    return [
+      self.__major,
+      self.__minor,
+      self.__maint,
+      self.__hotfix,
+      self.__build
+    ]
+
+  def __cmp__(self, other):
+    """
+    :type other MpackVersion
+
+    :raise TypeError
+    """
+    if other and not isinstance(other, self.__class__):
+      raise TypeError("Operand type is different from {0}".format(self.__class__.__name__))
+
+    r = 0
+    x = self.to_list()
+    y = other.to_list()
+
+    for i in range(0, len(x)):
+      r = x[i] - y[i]
+      if r != 0:
+        break
+
+    return 1 if r > 0 else -1 if r < 0 else 0
+
+  @classmethod
+  def parse(cls, mpack_version):
+    """
+      Parse string to mpack version
+
+      :type mpack_version str
+      :rtype MpackVersion
+      """
+    matcher = cls.validate(mpack_version)
+    return MpackVersion(
+      matcher.group("major"),
+      matcher.group("minor"),
+      matcher.group("maint"),
+      matcher.group("hotfix"),
+      matcher.group("build")
+    )
+
+
+  @classmethod
+  def parse_stack_version(cls, stack_version):
+    """
+      Parse string to mpack version
+
+      :type stack_version str
+      :rtype MpackVersion
+      """
+    matcher = cls.validate_stack_version(stack_version)
+    return MpackVersion(
+      matcher.group("major"),
+      matcher.group("minor"),
+      matcher.group("maint"),
+      matcher.group("hotfix"),
+      matcher.group("build")
+    )
+
+
+  @classmethod
+  def validate_stack_version(cls, stack_version):
+    """
+    Check if provided version is valid. If version is valid will return match object
+    or will raise exception.
+
+    :param stack_version version to check
+    :type stack_version str
+
+    :rtype __Match[T] | None
+
+    :raise ValueError
+    """
+
+    if not stack_version:
+      raise ValueError("Module version can't be empty or null")
+
+    version = stack_version.strip()
+
+    if not version:
+      raise ValueError("Module version can't be empty or null")
+
+    matcher = cls.__mpack_version_regex.match(version)
+
+    if not matcher:
+      matcher = cls.__mpack_legacy_stack_version_regex.match(version)
+      if not matcher:
+        raise ValueError("{0} is not a valid {1}".format(version, cls.__name__))
+    else:
+      if not matcher.group("hotfix"):
+        raise ValueError("{0} is not a valid {1}".format(version, cls.__name__))
+
+    return matcher
+
+
+  @classmethod
+  def validate(cls, mpack_version):
+    """
+    Check if provided version is valid. If version is valid will return match object
+    or will raise exception.
+
+    :param module_version version to check
+    :type module_version str
+
+    :rtype __Match[T] | None
+
+    :raise ValueError
+    """
+
+    if not mpack_version:
+      raise ValueError("Module version can't be empty or null")
+
+    version = mpack_version.strip()
+
+    if not version:
+      raise ValueError("Module version can't be empty or null")
+
+    matcher = cls.__mpack_version_regex.match(version)
+
+    if not matcher:
+      raise ValueError("{0} is not a valid {1}".format(version, cls.__name__))
+
+    return matcher
+
+  @property
+  def major(self):
+    return self.__major
+
+  @property
+  def minor(self):
+    return self.__minor
+
+  @property
+  def maint(self):
+    return self.__maint
+
+  @property
+  def hotfix(self):
+    return self.__hotfix
+
+  @property
+  def build(self):
+    return self.__build
+
+
+
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/ModuleVersion.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/ModuleVersion.java
new file mode 100644
index 0000000..520eac7
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/ModuleVersion.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.utils;
+
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * This class should be used to compare module(service) versions.
+ * Base method which should be used is parse(..), This method will validate and parse
+ * version which you will pass as parameter, and return object of current class with
+ * parsed version. Same thing you should do with another version, with which you are
+ * planning to compare previous one. After that, use method compare to get final result.
+ */
+
+public class ModuleVersion implements Comparable<ModuleVersion> {
+
+  // RE for different version formats like N.N.N.N-bN, N.N.N-hN-bN
+  private static final String VERSION_WITH_HOTFIX_AND_BUILD_PATTERN = "^([0-9]+).([0-9]+).([0-9]+).([0-9]+)-h([0-9]+)-b([0-9]+)";
+  private static final String VERSION_WITH_BUILD_PATTERN = "^([0-9]+).([0-9]+).([0-9]+).([0-9]+)-b([0-9]+)";
+
+  // Patterns for previous RE
+  private static final Pattern PATTERN_WITH_HOTFIX = Pattern.compile(VERSION_WITH_HOTFIX_AND_BUILD_PATTERN);
+  private static final Pattern PATTERN_WITHOUT_HOTFIX = Pattern.compile(VERSION_WITH_BUILD_PATTERN);
+
+  // Parts of version
+  private int apacheMajor;
+  private int apacheMinor;
+  private int internalMinor;
+  private int internalMaint;
+  private int hotfix;
+  private int build;
+
+
+  public ModuleVersion(int apacheMajor, int apacheMinor, int internalMinor, int internalMaint, int hotfix, int build) {
+    this.apacheMajor = apacheMajor;
+    this.apacheMinor = apacheMinor;
+    this.internalMinor = internalMinor;
+    this.internalMaint = internalMaint;
+    this.hotfix = hotfix;
+    this.build = build;
+  }
+
+  /**
+   * Method which will parse module version
+   * which user passed as parameter. Also
+   * in this method version will be validated.
+   * @param moduleVersion string
+   * @return MpackVersion instance which contains parsed version
+   * */
+  public static ModuleVersion parse(String moduleVersion) {
+    Matcher versionMatcher = validateModuleVersion(moduleVersion);
+    ModuleVersion result = null;
+
+    if (versionMatcher.pattern().pattern().equals(VERSION_WITH_HOTFIX_AND_BUILD_PATTERN)) {
+      result = new ModuleVersion(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)),
+              Integer.parseInt(versionMatcher.group(3)), Integer.parseInt(versionMatcher.group(4)),
+              Integer.parseInt(versionMatcher.group(5)), Integer.parseInt(versionMatcher.group(6)));
+
+    } else {
+      result = new ModuleVersion(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)),
+              Integer.parseInt(versionMatcher.group(3)), Integer.parseInt(versionMatcher.group(4)), 0,
+              Integer.parseInt(versionMatcher.group(5)));
+
+    }
+
+    return result;
+  }
+
+  /**
+   * Method validate module version not to be
+   * empty or null. Also check if passed version
+   * has valid format.
+   * @param version string
+   * @return Matcher for passed version
+   * @throws IllegalArgumentException() if version empty/null/not valid
+   */
+  private static Matcher validateModuleVersion(String version) {
+    if (StringUtils.isEmpty(version)) {
+      throw new IllegalArgumentException("Module version can't be empty or null");
+    }
+
+    String moduleVersion = StringUtils.trim(version);
+
+    Matcher versionMatcher = PATTERN_WITH_HOTFIX.matcher(moduleVersion);
+    if (!versionMatcher.find()) {
+      versionMatcher = PATTERN_WITHOUT_HOTFIX.matcher(moduleVersion);
+      if (!versionMatcher.find()) {
+        throw new IllegalArgumentException("Wrong format for module version, should be N.N.N.N-bN or N.N.N-hN-bN");
+      }
+    }
+
+    return versionMatcher;
+  }
+
+  @Override
+  public int compareTo(ModuleVersion other) {
+    int result = this.apacheMajor - other.apacheMajor;
+    if(result == 0) {
+      result = this.apacheMinor - other.apacheMinor;
+      if(result == 0) {
+        result = this.internalMinor - other.internalMinor;
+        if(result == 0) {
+          result = this.internalMaint - other.internalMaint;
+          if(result == 0) {
+            result = this.hotfix - other.hotfix;
+            if(result == 0) {
+              result = this.build - other.build;
+            }
+          }
+        }
+      }
+    }
+    return result > 0 ? 1 : result < 0 ? -1 : 0;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ModuleVersion that = (ModuleVersion) o;
+
+    if (apacheMajor != that.apacheMajor) return false;
+    if (apacheMinor != that.apacheMinor) return false;
+    if (build != that.build) return false;
+    if (hotfix != that.hotfix) return false;
+    if (internalMaint != that.internalMaint) return false;
+    if (internalMinor != that.internalMinor) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = apacheMajor;
+    result = 31 * result + apacheMinor;
+    result = 31 * result + internalMinor;
+    result = 31 * result + internalMaint;
+    result = 31 * result + hotfix;
+    result = 31 * result + build;
+    return result;
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/MpackVersion.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/MpackVersion.java
new file mode 100644
index 0000000..ad3fdc8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/MpackVersion.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.utils;
+
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * This class should be used to compare mpack and stack versions.
+ * Base method which should be used is parse/parseStackVersion, depends
+ * on which versions you want to compare. This method will validate and parse
+ * version which you will pass as parameter, and return object of current class with
+ * parsed version. Same thing you should do with another version, with which you are
+ * planning to compare previous one. After that, use method compare to get final result.
+ */
+
+public class MpackVersion implements Comparable<MpackVersion> {
+
+  // RE for different version formats like N.N.N-hN-bN, N.N.N-bN, N.N.N.N-N
+  private final static String VERSION_WITH_HOTFIX_AND_BUILD_PATTERN = "^([0-9]+).([0-9]+).([0-9]+)-h([0-9]+)-b([0-9]+)";
+  private final static String VERSION_WITH_BUILD_PATTERN = "^([0-9]+).([0-9]+).([0-9]+)-b([0-9]+)";
+  private final static String LEGACY_STACK_VERSION_PATTERN = "^([0-9]+).([0-9]+).([0-9]+).([0-9]+)-([0-9]+)";
+
+  // Patterns for previous RE
+  private final static Pattern PATTERN_WITH_HOTFIX = Pattern.compile(VERSION_WITH_HOTFIX_AND_BUILD_PATTERN);
+  private final static Pattern PATTERN_LEGACY_STACK_VERSION = Pattern.compile(LEGACY_STACK_VERSION_PATTERN);
+  private final static Pattern PATTERN_WITHOUT_HOTFIX = Pattern.compile(VERSION_WITH_BUILD_PATTERN);
+
+  // Parts of version
+  private int major;
+  private int minor;
+  private int maint;
+  private int hotfix;
+  private int build;
+
+
+  public MpackVersion(int major, int minor, int maint, int hotfix, int build) {
+    this.major = major;
+    this.minor = minor;
+    this.maint = maint;
+    this.hotfix = hotfix;
+    this.build = build;
+  }
+
+  /**
+   * Method which will parse mpack version
+   * which user passed as parameter. Also
+   * in this method version will be validated.
+   * @param mpackVersion string
+   * @return MpackVersion instance which contains parsed version
+   * */
+  public static MpackVersion parse(String mpackVersion) {
+    Matcher versionMatcher = validateMpackVersion(mpackVersion);
+    MpackVersion result = null;
+
+    if (versionMatcher.pattern().pattern().equals(VERSION_WITH_BUILD_PATTERN)) {
+      result = new MpackVersion(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)),
+              Integer.parseInt(versionMatcher.group(3)), 0, Integer.parseInt(versionMatcher.group(4)));
+
+    } else {
+      result = new MpackVersion(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)),
+              Integer.parseInt(versionMatcher.group(3)), Integer.parseInt(versionMatcher.group(4)), Integer.parseInt(versionMatcher.group(5)));
+
+    }
+
+    return result;
+  }
+
+  /**
+   * Method which will parse stack version
+   * which user passed as parameter. Also
+   * in this method version will be validated.
+   * @param stackVersion string
+   * @return MpackVersion instance which contains parsed version
+   * */
+  public static MpackVersion parseStackVersion(String stackVersion) {
+    Matcher versionMatcher = validateStackVersion(stackVersion);
+    MpackVersion result = new MpackVersion(Integer.parseInt(versionMatcher.group(1)), Integer.parseInt(versionMatcher.group(2)),
+          Integer.parseInt(versionMatcher.group(3)), Integer.parseInt(versionMatcher.group(4)), Integer.parseInt(versionMatcher.group(5)));
+
+    return result;
+  }
+
+  /**
+   * Method validate stack version not to be
+   * empty or null. Also check if passed version
+   * has valid format.
+   * @param version string
+   * @return Matcher for passed version
+   * @throws IllegalArgumentException() if version empty/null/not valid
+   */
+  private static Matcher validateStackVersion(String version) {
+    if (StringUtils.isEmpty(version)) {
+      throw new IllegalArgumentException("Stack version can't be empty or null");
+    }
+
+    String stackVersion = StringUtils.trim(version);
+
+    Matcher versionMatcher = PATTERN_WITH_HOTFIX.matcher(stackVersion);
+    if (!versionMatcher.find()) {
+      versionMatcher = PATTERN_LEGACY_STACK_VERSION.matcher(stackVersion);
+      if (!versionMatcher.find()) {
+        throw new IllegalArgumentException("Wrong format for stack version, should be N.N.N.N-N or N.N.N-hN-bN");
+      }
+    }
+
+    return versionMatcher;
+  }
+
+  /**
+   * Method validate mpack version not to be
+   * empty or null. Also check if passed version
+   * has valid format.
+   * @param version string
+   * @return Matcher for passed version
+   * @throws IllegalArgumentException() if version empty/null/not valid
+   */
+  private static Matcher validateMpackVersion(String version) {
+    if (StringUtils.isEmpty(version)) {
+      throw new IllegalArgumentException("Mpack version can't be empty or null");
+    }
+
+    String mpackVersion = StringUtils.trim(version);
+
+    Matcher versionMatcher = PATTERN_WITH_HOTFIX.matcher(mpackVersion);
+    if (!versionMatcher.find()) {
+      versionMatcher = PATTERN_WITHOUT_HOTFIX.matcher(mpackVersion);
+      if (!versionMatcher.find()) {
+        throw new IllegalArgumentException("Wrong format for mpack version, should be N.N.N-bN or N.N.N-hN-bN");
+      }
+    }
+
+    return versionMatcher;
+  }
+
+  @Override
+  public int compareTo(MpackVersion other) {
+    int result = this.major - other.major;
+    if(result == 0) {
+      result = this.minor - other.minor;
+      if(result == 0) {
+        result = this.maint - other.maint;
+        if(result == 0) {
+          result = this.hotfix - other.hotfix;
+          if(result == 0) {
+            result = this.build - other.build;
+          }
+        }
+      }
+    }
+    return result > 0 ? 1 : result < 0 ? -1 : 0;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    MpackVersion that = (MpackVersion) o;
+
+    if (build != that.build) return false;
+    if (hotfix != that.hotfix) return false;
+    if (maint != that.maint) return false;
+    if (major != that.major) return false;
+    if (minor != that.minor) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = major;
+    result = 31 * result + minor;
+    result = 31 * result + maint;
+    result = 31 * result + hotfix;
+    result = 31 * result + build;
+    return result;
+  }
+}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/utils/TestVersionUtils.java b/ambari-server/src/test/java/org/apache/ambari/server/utils/TestVersionUtils.java
index 42d321a..08c0a06 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/utils/TestVersionUtils.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/utils/TestVersionUtils.java
@@ -26,6 +26,10 @@ import junit.framework.Assert;
 
 public class TestVersionUtils {
 
+  private static final String MODULE_ERR_MESSAGE = "Module version can't be empty or null";
+  private static final String STACK_ERR_MESSAGE = "Stack version can't be empty or null";
+  private static final String MPACK_ERR_MESSAGE = "Mpack version can't be empty or null";
+
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
@@ -168,4 +172,142 @@ public class TestVersionUtils {
     expectedException.expectMessage("maxLengthToCompare cannot be less than 0");
     VersionUtils.compareVersions("2", "1", -1);
   }
+
+  @Test
+  public void testMpackVersionWithNotValidVersions() {
+    String errMessage = null;
+    try {
+      MpackVersion.parse(null);
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(MPACK_ERR_MESSAGE, errMessage);
+
+
+    try {
+      errMessage = null;
+      MpackVersion.parse("");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(MPACK_ERR_MESSAGE, errMessage);
+
+    try {
+      errMessage = null;
+      MpackVersion.parse("1.2.3.4-b10");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals("Wrong format for mpack version, should be N.N.N-bN or N.N.N-hN-bN", errMessage);
+  }
+
+
+  @Test
+  public void testStackVersionWithNotValidVersions() {
+    String errMessage = null;
+    try {
+      errMessage = null;
+      MpackVersion.parseStackVersion(null);
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(STACK_ERR_MESSAGE, errMessage);
+
+
+    try {
+      errMessage = null;
+      MpackVersion.parseStackVersion("");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(STACK_ERR_MESSAGE, errMessage);
+
+    try {
+      errMessage = null;
+      MpackVersion.parseStackVersion("1.2.3-10");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals("Wrong format for stack version, should be N.N.N.N-N or N.N.N-hN-bN", errMessage);
+  }
+
+
+  @Test
+  public void testModuleVersionWithNotValidVersions() {
+    String errMessage = null;
+    try {
+      errMessage = null;
+      ModuleVersion.parse(null);
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(MODULE_ERR_MESSAGE, errMessage);
+
+
+    try {
+      errMessage = null;
+      ModuleVersion.parse("");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals(MODULE_ERR_MESSAGE, errMessage);
+
+
+    try {
+      errMessage = null;
+      ModuleVersion.parse("1.2.3-10");
+    } catch (IllegalArgumentException e) {
+      errMessage = e.getMessage();
+    }
+    Assert.assertEquals("Wrong format for module version, should be N.N.N.N-bN or N.N.N-hN-bN", errMessage);
+  }
+
+  @Test
+  public void testMpackVersionWithValidVersions() {
+    Assert.assertEquals(1, MpackVersion.parse("1.2.3-h10-b10").compareTo(MpackVersion.parse("1.2.3-b888")));
+    Assert.assertEquals(1, MpackVersion.parse("2.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-b888")));
+    Assert.assertEquals(1, MpackVersion.parse("1.3.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-b888")));
+    Assert.assertEquals(1, MpackVersion.parse("1.2.4-h0-b10").compareTo(MpackVersion.parse("1.2.3-b888")));
+    Assert.assertEquals(1, MpackVersion.parse("1.2.3-h0-b1000").compareTo(MpackVersion.parse("1.2.3-b888")));
+    Assert.assertEquals(0, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-b10")));
+    Assert.assertEquals(0, MpackVersion.parse("1.2.3-b10").compareTo(MpackVersion.parse("1.2.3-b10")));
+    Assert.assertEquals(0, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-h0-b10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.4-b10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.3.3-b10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("2.2.3-b10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-h1-b10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h0-b10").compareTo(MpackVersion.parse("1.2.3-h0-b11")));
+  }
+
+  @Test
+  public void testStackVersionWithValidVersions() {
+    Assert.assertEquals(1, MpackVersion.parse("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("1.2.3.4-888")));
+    Assert.assertEquals(1, MpackVersion.parseStackVersion("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("1.2.3.4-888")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("1.2.3.11-888")));
+    Assert.assertEquals(1, MpackVersion.parseStackVersion("1.2.3.4-999").compareTo(MpackVersion.parseStackVersion("1.2.3.4-888")));
+    Assert.assertEquals(0, MpackVersion.parseStackVersion("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("1.2.3-h10-b10")));
+    Assert.assertEquals(0, MpackVersion.parseStackVersion("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("1.2.3.10-10")));
+    Assert.assertEquals(-1, MpackVersion.parse("1.2.3-h10-b10").compareTo(MpackVersion.parseStackVersion("2.2.3.4-888")));
+  }
+
+  @Test
+  public void testModuleVersionWithValidVersions() {
+    Assert.assertEquals(1, ModuleVersion.parse("1.2.3.4-h10-b888").compareTo(ModuleVersion.parse("1.2.3.4-b888")));
+    Assert.assertEquals(1, ModuleVersion.parse("1.2.3.5-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(1, ModuleVersion.parse("1.2.4.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(1, ModuleVersion.parse("1.3.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(1, ModuleVersion.parse("2.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(1, ModuleVersion.parse("1.2.3.4-h0-b11").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+
+    Assert.assertEquals(0, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(0, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-h0-b10")));
+    Assert.assertEquals(0, ModuleVersion.parse("1.2.3.4-b10").compareTo(ModuleVersion.parse("1.2.3.4-b10")));
+    Assert.assertEquals(0, ModuleVersion.parse("1.2.3.4-b10").compareTo(ModuleVersion.parse("1.2.3.4-h0-b10")));
+
+    Assert.assertEquals(-1, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.4-b888")));
+    Assert.assertEquals(-1, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.3.5-b10")));
+    Assert.assertEquals(-1, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.2.4.4-b10")));
+    Assert.assertEquals(-1, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("1.3.3.4-b10")));
+    Assert.assertEquals(-1, ModuleVersion.parse("1.2.3.4-h0-b10").compareTo(ModuleVersion.parse("2.2.3.4-b10")));
+  }
 }
diff --git a/ambari-server/src/test/python/TestVersion.py b/ambari-server/src/test/python/TestVersion.py
index a8f4c65..0b66478 100644
--- a/ambari-server/src/test/python/TestVersion.py
+++ b/ambari-server/src/test/python/TestVersion.py
@@ -16,8 +16,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 
-from unittest import TestCase
 import os
+from unittest import TestCase
+from resource_management.libraries.functions.mpack_version import MpackVersion
+from resource_management.libraries.functions.module_version import ModuleVersion
 
 
 class TestVersion(TestCase):
@@ -80,4 +82,98 @@ class TestVersion(TestCase):
     except ValueError:
       pass
     else:
-      self.fail("Did not raise exception")
\ No newline at end of file
+      self.fail("Did not raise exception")
+
+
+  def test_mpack_version(self):
+    try:
+      MpackVersion.parse("")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      MpackVersion.parse(None)
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      MpackVersion.parse_stack_version("")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      MpackVersion.parse_stack_version(None)
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      ModuleVersion.parse("")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      ModuleVersion.parse(None)
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+
+    try:
+      MpackVersion.parse("1.2.3.4-h1-b1")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    try:
+      MpackVersion.parse_stack_version("1.1.1.1.1-1")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+
+    try:
+      ModuleVersion.parse("1.1.1.1-h1")
+    except ValueError:
+      pass
+    else:
+      self.fail("Did not raise exception")
+
+    m1 = MpackVersion.parse("1.2.3-h1-b2")
+    m2 = MpackVersion.parse("1.2.3-b2")
+    self.assertTrue(m1 > m2)
+
+    m1 = MpackVersion.parse("1.2.3-h1-b2")
+    m2 = MpackVersion.parse_stack_version("1.2.3.4-33")
+    self.assertTrue(m1 < m2)
+
+    m1 = MpackVersion.parse("1.2.3-h0-b10")
+    m2 = MpackVersion.parse("1.2.3-b10")
+    self.assertTrue(m1 == m2)
+
+    m1 = ModuleVersion.parse("1.2.3.4-h10-b10")
+    m2 = ModuleVersion.parse("1.2.3.4-b888")
+    self.assertTrue(m1 > m2)
+
+    m1 = ModuleVersion.parse("1.2.3.5-h10-b10")
+    m2 = ModuleVersion.parse("1.2.3.4-b888")
+    self.assertTrue(m1 > m2)
+
+    m1 = ModuleVersion.parse("1.2.3.4-h0-b10")
+    m2 = ModuleVersion.parse("1.2.3.4-b10")
+    self.assertTrue(m1 == m2)
+
+
+

-- 
To stop receiving notification emails like this one, please contact
vbrodetskyi@apache.org.