commit 937e35de09ccc516a6dede0b115267fb81050216
Merge: 89922d0 c2a97ce
Author: Klaus Aehlig <[email protected]>
Date: Fri May 9 11:56:48 2014 +0200
Merge branch 'stable-2.10' into stable-2.11
* stable-2.10
Test parallel instance ops and plain instances
Test parallel creation of DRBD instances
Test parallel job submission performance
Test parallel instance query operations
Test parallel instance operations
Test parallel instance modification
Test parallel node-count instance creation
Test parallel instance creation and removal
Fail in replace-disks if attaching disks fails
Add a basic test for --restricted-migration
Describe the --restricted-migration option
Support restricted migration
Add an option for restricted migration
Add an example for node evacuation
Add a test for parsing version strings
* stable-2.9
KVM: set IFF_ONE_QUEUE on created tap interfaces
Add configure option to pass GHC flags
Conflicts:
Makefile.am
configure.ac
qa/qa_utils.py
Resolution:
Take ALL the additions
Semantical conflict:
make hsqueeze use the modified tryBalance signature
diff --cc Makefile.am
index efacbf8,57e12a7..5eff41b
--- a/Makefile.am
+++ b/Makefile.am
@@@ -1022,50 -972,55 +1024,50 @@@ install-exec-hook
for role in $(HS_BIN_ROLES); do \
$(LN_S) -f htools $(DESTDIR)$(bindir)/$$role ; \
done
+
+HS_SRCS = $(HS_LIBTESTBUILT_SRCS)
+
+HS_MAKEFILE_GHC_SRCS = $(HS_SRC_PROGS:%=%.hs)
+if WANT_HSTESTS
+HS_MAKEFILE_GHC_SRCS += $(HS_TEST_PROGS:%=%.hs)
endif
+Makefile.ghc: $(HS_MAKEFILE_GHC_SRCS) Makefile \
+ | $(built_base_sources) $(HS_BUILT_SRCS)
+ $(GHC) -M -dep-makefile $@ -dep-suffix $(HTEST_SUFFIX) $(HFLAGS)
-itest/hs \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA)
$(HS_MAKEFILE_GHC_SRCS)
++ $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA_COMBINED)
$(HS_MAKEFILE_GHC_SRCS)
-$(HS_ALL_PROGS): %: %.hs $(HS_LIBTESTBUILT_SRCS) Makefile
- @if [ "$(notdir $@)" = "test" ] && [ "$(HS_NODEV)" ]; then \
- echo "Error: cannot run unittests without the development" \
- " libraries (see devnotes.rst)" 1>&2; \
- exit 1; \
- fi
- @rm -f $(notdir $@).tix
- $(GHC) --make \
- $(HFLAGS) \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) \
- -osuf $(notdir $@).o -hisuf $(notdir $@).hi \
- $(HEXTRA_COMBINED) $(HEXTRA_INT) $@
- @touch "$@"
+@include_makefile_ghc@
-# for the test/hs/htest binary, we need to enable profiling/coverage
-test/hs/htest: HEXTRA_INT=-fhpc -itest/hs
+%.o:
+ @echo '[GHC]: $@ <- $^'
+ @$(GHC) -c $(HFLAGS) \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA) $(@:%.o=%.hs)
++ $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA_COMBINED)
$(@:%.o=%.hs)
-# we compile the hpc-htools binary with the program coverage
-test/hs/hpc-htools: HEXTRA_INT=-fhpc
+%.$(HTEST_SUFFIX)_o:
+ @echo '[GHC]: $@ <- $^'
+ @$(GHC) -c $(HTEST_FLAGS) \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA)
$(@:%.$(HTEST_SUFFIX)_o=%.hs)
++ $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA_COMBINED)
$(@:%.$(HTEST_SUFFIX)_o=%.hs)
-# we compile the hpc-mon-collector binary with the program coverage
-test/hs/hpc-mon-collector: HEXTRA_INT=-fhpc
+%.hi: %.o ;
+%.$(HTEST_SUFFIX)_hi: %.$(HTEST_SUFFIX)_o ;
-# test dependency
-test/hs/offline-test.sh: test/hs/hpc-htools test/hs/hpc-mon-collector
+$(HS_SRC_PROGS): %: %.o | stamp-directories
+ $(GHC) $(HFLAGS) \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA) --make $(@:%=%.hs)
++ $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA_COMBINED) --make
$(@:%=%.hs)
+ @rm -f $(notdir $@).tix
+ @touch "$@"
-# rules for building profiling-enabled versions of the haskell
-# programs: hs-prof does the full two-step build, whereas
-# hs-prof-quick does only the final rebuild (hs-prof must have been
-# run before)
-.PHONY: hs-prof hs-prof-quick
-hs-prof:
- @if [ -z "$(TARGET)" ]; then \
- echo "You need to define TARGET when running this rule" 1>&2; \
- exit 1; \
- fi
- $(MAKE) $(AM_MAKEFLAGS) clean
- $(MAKE) $(AM_MAKEFLAGS) $(TARGET) HEXTRA="-osuf o"
- rm -f $(HS_ALL_PROGS)
- $(MAKE) $(AM_MAKEFLAGS) hs-prof-quick
-
-hs-prof-quick:
- @if [ -z "$(TARGET)" ]; then \
- echo "You need to define TARGET when running this rule" 1>&2; \
+$(HS_TEST_PROGS): %: %.$(HTEST_SUFFIX)_o \
+ | stamp-directories $(BUILT_PYTHON_SOURCES)
+ @if [ "$(HS_NODEV)" ]; then \
+ echo "Error: cannot run unittests without the development" \
+ " libraries (see devnotes.rst)" 1>&2; \
exit 1; \
fi
- $(MAKE) $(AM_MAKEFLAGS) $(TARGET) HEXTRA="-osuf prof_o -prof -auto-all"
+ $(GHC) $(HTEST_FLAGS) \
- $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA) --make $(@:%=%.hs)
++ $(HS_PARALLEL3) $(HS_REGEX_PCRE) $(HEXTRA_COMBINED) --make
$(@:%=%.hs)
+ @rm -f $(notdir $@).tix
+ @touch "$@"
dist_sbin_SCRIPTS = \
tools/ganeti-listrunner
@@@ -1311,9 -1261,9 +1314,10 @@@ TEST_FILES =
test/hs/shelltests/htools-dynutil.test \
test/hs/shelltests/htools-excl.test \
test/hs/shelltests/htools-hail.test \
+ test/hs/shelltests/htools-hbal-evac.test \
test/hs/shelltests/htools-hroller.test \
test/hs/shelltests/htools-hspace.test \
+ test/hs/shelltests/htools-hsqueeze.test \
test/hs/shelltests/htools-invalid.test \
test/hs/shelltests/htools-multi-group.test \
test/hs/shelltests/htools-no-backend.test \
diff --cc configure.ac
index 2db63d4,b4cb5f9..8942899
--- a/configure.ac
+++ b/configure.ac
@@@ -60,54 -60,15 +60,63 @@@ AC_ARG_ENABLE([symlinks]
AC_SUBST(INSTALL_SYMLINKS, $INSTALL_SYMLINKS)
AM_CONDITIONAL([INSTALL_SYMLINKS], [test "$INSTALL_SYMLINKS" = yes])
+# --enable-haskell-profiling
+AC_ARG_ENABLE([haskell-profiling],
+ [AS_HELP_STRING([--enable-haskell-profiling],
+ m4_normalize([enable profiling for Haskell binaries
+ (default: disabled)]))],
+ [[if test "$enableval" != yes; then
+ HPROFILE=no
+ else
+ HPROFILE=yes
+ fi
+ ]],
+ [HPROFILE=no
+ ])
+AC_SUBST(HPROFILE, $HPROFILE)
+AM_CONDITIONAL([HPROFILE], [test "$HPROFILE" = yes])
+
+# --enable-haskell-coverage
+AC_ARG_ENABLE([haskell-coverage],
+ [AS_HELP_STRING([--enable-haskell-coverage],
+ m4_normalize([enable coverage for Haskell binaries
+ (default: disabled)]))],
+ [[if test "$enableval" != yes; then
+ HCOVERAGE=no
+ else
+ HCOVERAGE=yes
+ fi
+ ]],
+ [HCOVERAGE=no
+ ])
+AC_SUBST(HCOVERAGE, $HCOVERAGE)
+AM_CONDITIONAL([HCOVERAGE], [test "$HCOVERAGE" = yes])
+
+# --enable-haskell-tests
+AC_ARG_ENABLE([haskell-tests],
+ [AS_HELP_STRING([--enable-haskell-tests],
+ m4_normalize([enable additinal Haskell development test code
+ (default: disabled)]))],
+ [[if test "$enableval" != yes; then
+ HTEST=no
+ else
+ HTEST=yes
+ fi
+ ]],
+ [HTEST=no
+ ])
+AC_SUBST(HTEST, $HTEST)
+AM_CONDITIONAL([HTEST], [test "$HTEST" = yes])
+
+ # --with-haskell-flags=
+ AC_ARG_WITH([haskell-flags],
+ [AS_HELP_STRING([--with-haskell-flags=FLAGS],
+ [Extra flags to pass to GHC]
+ )],
+ [hextra_configure="$withval"],
+ [hextra_configure=""])
+ AC_SUBST(HEXTRA_CONFIGURE, $hextra_configure)
+
# --with-ssh-initscript=...
AC_ARG_WITH([ssh-initscript],
[AS_HELP_STRING([--with-ssh-initscript=SCRIPT],
diff --cc man/hbal.rst
index d5e009d,568830a..1be9828
--- a/man/hbal.rst
+++ b/man/hbal.rst
@@@ -31,8 -31,8 +31,9 @@@ Algorithm options
**[ \--no-instance-moves ]**
**[ -U *util-file* ]**
**[ \--ignore-dynu ]**
+**[ \--mond *yes|no* ]**
**[ \--evac-mode ]**
+ **[ \--restricted-migration ]**
**[ \--select-instances *inst...* ]**
**[ \--exclude-instances *inst...* ]**
diff --cc qa/ganeti-qa.py
index 1f9fe4a,6b6e584..e35d95f
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@@ -983,8 -955,10 +1021,10 @@@ def RunQa()
snode.Release()
qa_cluster.AssertClusterVerify()
- RunMonitoringTests()
+ RunTestBlock(RunMonitoringTests)
+ RunPerformanceTests()
+
RunTestIf("create-cluster", qa_node.TestNodeRemoveAll)
RunTestIf("cluster-destroy", qa_cluster.TestClusterDestroy)
diff --cc qa/qa_utils.py
index a8bc8a3,a520570..8e6d317
--- a/qa/qa_utils.py
+++ b/qa/qa_utils.py
@@@ -875,8 -884,17 +887,24 @@@ def ParseIPolicy(policy)
return (ret_policy, ret_specs)
+def UsesIPv6Connection(host, port):
+ """Returns True if the connection to a given host/port could go through
IPv6.
+
+ """
+ return any(t[0] == socket.AF_INET6 for t in socket.getaddrinfo(host, port))
++
++
+ def TimedeltaToTotalSeconds(td):
+ """Returns the total seconds in a C{datetime.timedelta} object.
+
+ This performs the same task as the C{datetime.timedelta.total_seconds()}
+ method which is present in Python 2.7 onwards.
+
+ @type td: datetime.timedelta
+ @param td: timedelta object to convert
+ @rtype float
+ @return: total seconds in the timedelta object
+
+ """
+ return ((td.microseconds + (td.seconds + td.days * 24.0 * 3600.0) * 10 **
6) /
+ 10 ** 6)
diff --cc src/Ganeti/HTools/Program/Hsqueeze.hs
index 360507d,0000000..90cd419
mode 100644,000000..100644
--- a/src/Ganeti/HTools/Program/Hsqueeze.hs
+++ b/src/Ganeti/HTools/Program/Hsqueeze.hs
@@@ -1,282 -1,0 +1,282 @@@
+{-| Node freeing scheduler
+
+-}
+
+{-
+
+Copyright (C) 2013 Google Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+-}
+
+module Ganeti.HTools.Program.Hsqueeze
+ (main
+ , options
+ , arguments
+ ) where
+
+import Control.Applicative
+import Control.Monad
+import Data.Function
+import Data.List
+import Data.Maybe
+import qualified Data.IntMap as IntMap
+import Text.Printf (printf)
+
+import Ganeti.BasicTypes
+import Ganeti.Common
+import Ganeti.HTools.CLI
+import qualified Ganeti.HTools.Container as Container
+import qualified Ganeti.HTools.Cluster as Cluster
+import Ganeti.HTools.ExtLoader
+import qualified Ganeti.HTools.Instance as Instance
+import Ganeti.HTools.Loader
+import qualified Ganeti.HTools.Node as Node
+import Ganeti.HTools.Types
+import Ganeti.Utils
+
+-- | Options list and functions.
+options :: IO [OptType]
+options = do
+ luxi <- oLuxiSocket
+ return
+ [ luxi
+ , oDataFile
+ , oMinResources
+ , oTargetResources
+ , oSaveCluster
+ , oPrintCommands
+ , oVerbose
+ , oNoHeaders
+ ]
+
+-- | The list of arguments supported by the program.
+arguments :: [ArgCompletion]
+arguments = []
+
+-- | The tag-prefix indicating that hsqueeze should consider a node
+-- as being standby.
+standbyPrefix :: String
+standbyPrefix = "htools:standby:"
+
+-- | Predicate of having a standby tag.
+hasStandbyTag :: Node.Node -> Bool
+hasStandbyTag = any (standbyPrefix `isPrefixOf`) . Node.nTags
+
+-- | Within a cluster configuration, decide if the node hosts only
+-- externally-mirrored instances.
+onlyExternal :: (Node.List, Instance.List) -> Node.Node -> Bool
+onlyExternal (_, il) nd =
+ not
+ . any (Instance.usesLocalStorage . flip Container.find il)
+ $ Node.pList nd
+
+-- | Predicate of not being secondary node for any instance
+noSecondaries :: Node.Node -> Bool
+noSecondaries = null . Node.sList
+
+-- | Predicate whether, in a configuration, all running instances are on
+-- online nodes.
+allInstancesOnOnlineNodes :: (Node.List, Instance.List) -> Bool
+allInstancesOnOnlineNodes (nl, il) =
+ all (not . Node.offline . flip Container.find nl . Instance.pNode)
+ . IntMap.elems
+ $ il
+
+-- | Predicate whether, in a configuration, each node has enough resources
+-- to additionally host the given instance.
+allNodesCapacityFor :: Instance.Instance -> (Node.List, Instance.List) -> Bool
+allNodesCapacityFor inst (nl, _) =
+ all (isOk . flip Node.addPri inst) . IntMap.elems $ nl
+
+-- | Balance a configuration, possible for 0 steps, till no further
improvement
+-- is possible.
+balance :: (Node.List, Instance.List)
+ -> ((Node.List, Instance.List), [MoveJob])
+balance (nl, il) =
+ let ini_cv = Cluster.compCV nl
+ ini_tbl = Cluster.Table nl il ini_cv []
- balanceStep tbl = Cluster.tryBalance tbl True True False 0.0 0.0
++ balanceStep tbl = Cluster.tryBalance tbl True True False False 0.0 0.0
+ bTables = map fromJust . takeWhile isJust
+ $ iterate (>>= balanceStep) (Just ini_tbl)
+ (Cluster.Table nl' il' _ _) = last bTables
+ moves = zip bTables (drop 1 bTables) >>= Cluster.getMoves
+ in ((nl', il'), reverse moves)
+
+-- | In a configuration, mark a node as online or offline.
+onlineOfflineNode :: Bool -> (Node.List, Instance.List) -> Ndx ->
+ (Node.List, Instance.List)
+onlineOfflineNode offline (nl, il) ndx =
+ let nd = Container.find ndx nl
+ nd' = Node.setOffline nd offline
+ nl' = Container.add ndx nd' nl
+ in (nl', il)
+
+-- | Offline or online a list nodes, and return the state after a balancing
+-- attempt together with the sequence of moves that lead there.
+onlineOfflineNodes :: Bool -> [Ndx] -> (Node.List, Instance.List)
+ -> ((Node.List, Instance.List), [MoveJob])
+onlineOfflineNodes offline ndxs conf =
+ let conf' = foldl (onlineOfflineNode offline) conf ndxs
+ in balance conf'
+
+-- | Offline a list of nodes, and return the state after balancing with
+-- the sequence of moves that lead there.
+offlineNodes :: [Ndx] -> (Node.List, Instance.List)
+ -> ((Node.List, Instance.List), [MoveJob])
+offlineNodes = onlineOfflineNodes True
+
+-- | Online a list of nodes, and return the state after balancing with
+-- the sequence of moves that lead there.
+onlineNodes :: [Ndx] -> (Node.List, Instance.List)
+ -> ((Node.List, Instance.List), [MoveJob])
+onlineNodes = onlineOfflineNodes False
+
+-- | Predicate on whether a list of nodes can be offlined or onlined
+-- simultaneously in a given configuration, while still leaving enough
+-- capacity on every node for the given instance.
+canOnlineOffline :: Bool -> Instance.Instance -> (Node.List, Instance.List)
+ -> [Node.Node] ->Bool
+canOnlineOffline offline inst conf nds =
+ let conf' = fst $ onlineOfflineNodes offline (map Node.idx nds) conf
+ in allInstancesOnOnlineNodes conf' && allNodesCapacityFor inst conf'
+
+-- | Predicate on whether a list of nodes can be offlined simultaneously.
+canOffline :: Instance.Instance -> (Node.List, Instance.List) ->
+ [Node.Node] -> Bool
+canOffline = canOnlineOffline True
+
+-- | Predicate on whether onlining a list of nodes suffices to get enough
+-- free resources for given instance.
+sufficesOnline :: Instance.Instance -> (Node.List, Instance.List)
+ -> [Node.Node] -> Bool
+sufficesOnline = canOnlineOffline False
+
+-- | Greedily offline the nodes, starting from the last element, and return
+-- the list of nodes that could simultaneously be offlined, while keeping
+-- the resources specified by an instance.
+greedyOfflineNodes :: Instance.Instance -> (Node.List, Instance.List)
+ -> [Node.Node] -> [Node.Node]
+greedyOfflineNodes _ _ [] = []
+greedyOfflineNodes inst conf (nd:nds) =
+ let nds' = greedyOfflineNodes inst conf nds
+ in if canOffline inst conf (nd:nds') then nd:nds' else nds'
+
+-- | Try to provide enough resources by onlining an initial segment of
+-- a list of nodes. Return Nothing, if even onlining all of them is not
+-- enough.
+tryOnline :: Instance.Instance -> (Node.List, Instance.List) -> [Node.Node]
+ -> Maybe [Node.Node]
+tryOnline inst conf = listToMaybe . filter (sufficesOnline inst conf) . inits
+
+-- | From a specification, name, and factor create an instance that uses that
+-- factor times the specification, rounded down.
+instanceFromSpecAndFactor :: String -> Double -> ISpec -> Instance.Instance
+instanceFromSpecAndFactor name f spec =
+ Instance.create name
+ (floor (f * fromIntegral (iSpecMemorySize spec)))
+ 0 []
+ (floor (f * fromIntegral (iSpecCpuCount spec)))
+ Running [] False Node.noSecondary Node.noSecondary DTExt
+ (floor (f * fromIntegral (iSpecSpindleUse spec)))
+ []
+
+-- | Main function.
+main :: Options -> [String] -> IO ()
+main opts args = do
+ unless (null args) $ exitErr "This program doesn't take any arguments."
+
+ let verbose = optVerbose opts
+ targetf = optTargetResources opts
+ minf = optMinResources opts
+
+ ini_cdata@(ClusterData _ nlf ilf _ ipol) <- loadExternalData opts
+
+ maybeSaveData (optSaveCluster opts) "original" "before hsqueeze run"
ini_cdata
+
+ let nodelist = IntMap.elems nlf
+ offlineCandidates =
+ sortBy (flip compare `on` length . Node.pList)
+ . filter (foldl (liftA2 (&&)) (const True)
+ [ not . Node.offline
+ , not . Node.isMaster
+ , noSecondaries
+ , onlyExternal (nlf, ilf)
+ ])
+ $ nodelist
+ onlineCandidates =
+ filter (liftA2 (&&) Node.offline hasStandbyTag) nodelist
+ conf = (nlf, ilf)
+ std = iPolicyStdSpec ipol
+ targetInstance = instanceFromSpecAndFactor "targetInstance" targetf std
+ minInstance = instanceFromSpecAndFactor "targetInstance" minf std
+ toOffline = greedyOfflineNodes targetInstance conf offlineCandidates
+ ((fin_off_nl, fin_off_il), off_mvs) =
+ offlineNodes (map Node.idx toOffline) conf
+ final_off_cdata =
+ ini_cdata { cdNodes = fin_off_nl, cdInstances = fin_off_il }
+ off_jobs = Cluster.splitJobs off_mvs
+ off_cmd =
+ Cluster.formatCmds off_jobs
+ ++ "\necho Tagging Commands\n"
+ ++ (toOffline >>= printf " gnt-node add-tags %s
htools:standby:auto\n"
+ . Node.alias)
+ ++ "\necho Power Commands\n"
+ ++ (toOffline >>= printf " gnt-node power -f off %s\n" . Node.alias)
+ toOnline = tryOnline minInstance conf onlineCandidates
+ nodesToOnline = fromMaybe onlineCandidates toOnline
+ ((fin_on_nl, fin_on_il), on_mvs) =
+ onlineNodes (map Node.idx nodesToOnline) conf
+ final_on_cdata =
+ ini_cdata { cdNodes = fin_on_nl, cdInstances = fin_on_il }
+ on_jobs = Cluster.splitJobs on_mvs
+ on_cmd =
+ "echo Power Commands\n"
+ ++ (nodesToOnline >>= printf " gnt-node power -f on %s\n" .
Node.alias)
+ ++ Cluster.formatCmds on_jobs
+
+ when (verbose > 1) . putStrLn
+ $ "Offline candidates: " ++ commaJoin (map Node.name offlineCandidates)
+
+ when (verbose > 1) . putStrLn
+ $ "Online candidates: " ++ commaJoin (map Node.name onlineCandidates)
+
+ if not (allNodesCapacityFor minInstance conf)
+ then do
+ unless (optNoHeaders opts) $
+ putStrLn "'Nodes to online'"
+ mapM_ (putStrLn . Node.name) nodesToOnline
+ when (verbose > 1 && isNothing toOnline) . putStrLn $
+ "Onlining all nodes will not yield enough capacity"
+ maybeSaveCommands "Commands to run:" opts on_cmd
+ maybeSaveData (optSaveCluster opts)
+ "squeezed" "after hsqueeze expansion" final_on_cdata
+ else
+ if null toOffline
+ then do
+ unless (optNoHeaders opts) $
+ putStrLn "'No action'"
+ maybeSaveCommands "Commands to run:" opts "echo Nothing to do"
+ maybeSaveData (optSaveCluster opts)
+ "squeezed" "after hsqueeze doing nothing" ini_cdata
+ else do
+ unless (optNoHeaders opts) $
+ putStrLn "'Nodes to offline'"
+ mapM_ (putStrLn . Node.name) toOffline
+ maybeSaveCommands "Commands to run:" opts off_cmd
+ maybeSaveData (optSaveCluster opts)
+ "squeezed" "after hsqueeze run" final_off_cdata
--
Klaus Aehlig
Google Germany GmbH, Dienerstr. 12, 80331 Muenchen
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschaeftsfuehrer: Graham Law, Christine Elizabeth Flores