merge
This commit is contained in:
commit
433392a724
19 changed files with 69 additions and 43 deletions
|
|
@ -57,7 +57,7 @@
|
|||
* Code in kill_session is removed where this fact was used to actually kill the client process.
|
||||
* XPATH canonical form implemented for NETCONF get and get-config. This means that all callbacks (including state callbacks) will have the prefixes that corresponds to module prefixes. If an xpath have other prefixes (or null as default), they will be translated to canonical form before any callbacks.
|
||||
* Example of a canonical form: `/a:x/a:y`, then symbols must belong to a yang module with prefix `a`.
|
||||
* Configure and test modification for better Freebsd port
|
||||
* FreeBSD modifications: Configure, makefiles and test scripts modification for Freebsd
|
||||
|
||||
### Corrected Bugs
|
||||
* See "Stricter handling of multi-namespace handling" in API-changes above.
|
||||
|
|
|
|||
12
Makefile.in
12
Makefile.in
|
|
@ -52,13 +52,19 @@ INSTALL = @INSTALL@
|
|||
INCLUDES = -I. -I@srcdir@ @INCLUDES@
|
||||
SHELL = /bin/sh
|
||||
|
||||
SUBDIRS = lib apps include etc yang
|
||||
SUBDIRS1 = include lib
|
||||
SUBDIRS2 = apps etc yang # without include lib for circular dependency
|
||||
SUBDIRS= $(SUBDIRS1) $(SUBDIRS2)
|
||||
|
||||
.PHONY: doc example all clean depend $(SUBDIRS) install loc TAGS .config.status docker test
|
||||
|
||||
all: $(SUBDIRS)
|
||||
all: $(SUBDIRS2)
|
||||
|
||||
$(SUBDIRS): include lib # Cannot build app before lib (for parallel make -j)
|
||||
# May cause circular include->include,lib
|
||||
$(SUBDIRS2): include lib # Cannot build app before lib (for parallel make -j)
|
||||
(cd $@ && $(MAKE) $(MFLAGS) all)
|
||||
|
||||
$(SUBDIRS1):
|
||||
(cd $@ && $(MAKE) $(MFLAGS) all)
|
||||
|
||||
depend:
|
||||
|
|
|
|||
|
|
@ -1393,8 +1393,10 @@ from_client_msg(clicon_handle h,
|
|||
/* NACM rpc operation exec validation */
|
||||
if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
|
||||
goto done;
|
||||
if (xnacm)
|
||||
if (xnacm){
|
||||
xml_free(xnacm);
|
||||
xnacm = NULL;
|
||||
}
|
||||
if (ret == 0) /* Not permitted and cbret set */
|
||||
goto reply;
|
||||
}
|
||||
|
|
@ -1439,6 +1441,8 @@ from_client_msg(clicon_handle h,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xnacm)
|
||||
xml_free(xnacm);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (xt)
|
||||
|
|
|
|||
|
|
@ -269,9 +269,11 @@ backend_accept_client(int fd,
|
|||
#else
|
||||
#error "Need getsockopt O_PEERCRED or getpeereid for unix socket peer cred"
|
||||
#endif
|
||||
if (name && (ce->ce_username = strdup(name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
if (name != NULL){
|
||||
if ((ce->ce_username = strdup(name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_INET:
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
|
|||
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
|
||||
|
||||
# Use this clixon lib for linking
|
||||
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
|
||||
# For dependency. A little strange that we rely on it being built in the src dir
|
||||
# even though it may exist in $(libdir). But the new version may not have been installed yet.
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
|
|||
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
|
||||
|
||||
# Use this clixon lib for linking
|
||||
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
|
||||
# For dependency
|
||||
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
|
||||
|
|
|
|||
|
|
@ -59,12 +59,12 @@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
|
|||
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
|
||||
|
||||
# Use this clixon lib for linking
|
||||
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
|
||||
# For dependency
|
||||
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
|
||||
|
||||
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB)
|
||||
LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB)
|
||||
|
||||
CPPFLAGS = @CPPFLAGS@ -fPIC
|
||||
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ api_root(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Status: 200 OK\r\n");
|
||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
||||
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
|
|||
CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||
CFLAGS = @CFLAGS@ -fPIC
|
||||
INSTALLFLAGS = @INSTALLFLAGS@
|
||||
with_restconf = @with_restconf@
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
|
|||
CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||
CFLAGS = @CFLAGS@ -fPIC
|
||||
INSTALLFLAGS = @INSTALLFLAGS@
|
||||
with_restconf = @with_restconf@
|
||||
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ distclean: clean
|
|||
.SUFFIXES: .c .o
|
||||
|
||||
.c.o: $(GENSRC)
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
rm -f $@ && $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
.PHONY: build.c
|
||||
build.c:
|
||||
|
|
|
|||
|
|
@ -233,6 +233,9 @@ wait_restconf(){
|
|||
err "restconf timeout $RCWAIT seconds"
|
||||
fi
|
||||
done
|
||||
if [ $valgrindtest -eq 3 ]; then
|
||||
sleep 2 # some problems with valgrind
|
||||
fi
|
||||
}
|
||||
|
||||
# Increment test number and print a nice string
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ memonce(){
|
|||
valgrindtest=3 # This means backend valgrind test
|
||||
sudo chmod 660 $valgrindfile
|
||||
sudo chown www-data $valgrindfile
|
||||
: ${RCWAIT:=10} # valgrind backend needs some time to get up
|
||||
: ${RCWAIT:=15} # valgrind backend needs some time to get up
|
||||
clixon_restconf="/usr/bin/valgrind --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile /www-data/clixon_restconf"
|
||||
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
|
|||
|
||||
# Now take large config file and write it via netconf to candidate
|
||||
new "netconf write large config"
|
||||
|
||||
expecteof_file "time $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# Here, there are $perfnr entries in candidate
|
||||
|
|
@ -106,12 +107,12 @@ expecteof_file "time -f %e $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply><
|
|||
|
||||
# Now commit it from candidate to running
|
||||
new "netconf commit large config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# Now commit it again from candidate (validation takes time when
|
||||
# comparing to existing)
|
||||
new "netconf commit large config again"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# Having a large db, get and put single entries many times
|
||||
# Note same entries in the range alreay there, db has same size
|
||||
|
|
@ -164,13 +165,13 @@ done } 2>&1 | awk '/real/ {print $2}'
|
|||
# Instead of many small entries, get one large in netconf and restconf
|
||||
# cli?
|
||||
new "netconf get large config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b></y><y><a>2</a><b>2</b></y><y><a>3</a><b>3</b></y>'
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b></y><y><a>2</a><b>2</b></y><y><a>3</a><b>3</b></y>'
|
||||
|
||||
new "restconf get large config"
|
||||
/usr/bin/time -f %e curl -sG http://localhost/restconf/data > /dev/null
|
||||
time curl -sG http://localhost/restconf/data > /dev/null
|
||||
|
||||
new "cli get large config"
|
||||
/usr/bin/time -f %e $clixon_cli -1f $cfg show config xml> /dev/null
|
||||
time $clixon_cli -1f $cfg show config xml> /dev/null
|
||||
|
||||
# Delete entries (last since entries are removed from db)
|
||||
# netconf
|
||||
|
|
@ -208,10 +209,10 @@ done
|
|||
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig2
|
||||
|
||||
new "netconf replace large list-leaf config"
|
||||
expecteof_file "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "$fconfig2" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof_file "time $clixon_netconf -qf $cfg" 0 "$fconfig2" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit large leaf-list config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf add $perfreq small leaf-list config"
|
||||
time -p for (( i=0; i<$perfreq; i++ )); do
|
||||
|
|
@ -220,13 +221,13 @@ time -p for (( i=0; i<$perfreq; i++ )); do
|
|||
done | $clixon_netconf -qf $cfg > /dev/null
|
||||
|
||||
new "netconf add small leaf-list config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><c>x</c></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><c>x</c></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit small leaf-list config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get large leaf-list config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><c>0</c><c>1</c>'
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><c>0</c><c>1</c>'
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
|
|
|||
|
|
@ -76,11 +76,11 @@ echo "</interfaces></config></edit-config></rpc>]]>]]>" >> $fconfig
|
|||
|
||||
# Now take large config file and write it via netconf to candidate
|
||||
new "netconf write large config"
|
||||
expecteof_file "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof_file "time $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# Now commit it from candidate to running
|
||||
new "netconf commit large config"
|
||||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# START actual tests
|
||||
# Having a large db, get single entries many times
|
||||
|
|
@ -128,13 +128,13 @@ done } 2>&1 | awk '/real/ {print $2}'
|
|||
|
||||
# Get config in one large get
|
||||
new "netconf get large config"
|
||||
/usr/bin/time -f %e echo "<rpc><get> <filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>" | $clixon_netconf -qf $cfg > /tmp/netconf
|
||||
time echo "<rpc><get> <filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>" | $clixon_netconf -qf $cfg > /tmp/netconf
|
||||
|
||||
new "restconf get large config"
|
||||
/usr/bin/time -f %e curl -sG http://localhost/restconf/data/ietf-interfaces:interfaces | wc
|
||||
time curl -sG http://localhost/restconf/data/ietf-interfaces:interfaces | wc
|
||||
|
||||
new "cli get large config"
|
||||
/usr/bin/time -f %e $clixon_cli -1f $cfg show state xml interfaces | wc
|
||||
time $clixon_cli -1f $cfg show state xml interfaces | wc
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
|
|
|||
|
|
@ -30,11 +30,16 @@ cat <<EOF > $cfg
|
|||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
||||
# Create a pre-set running, startup and (extra) config.
|
||||
# The configs are identified by an interface called run, startup, extra.
|
||||
# Depending on startup mode (init, none, running, or startup)
|
||||
# expect different output of an initial get-config of running
|
||||
# Arguments:
|
||||
# 1: startuser: Start backend as this user
|
||||
# 2: backend user: Drop to this after initial run as startuser
|
||||
# 3: expected user: Expected user after drop (or no drop then startuser)
|
||||
# 4: privileged mode (none, drop_perm, drop_temp)
|
||||
# 5: expect error: 0 or 1
|
||||
testrun(){
|
||||
startuser=$1
|
||||
beuser=$2
|
||||
|
|
@ -64,7 +69,7 @@ testrun(){
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
|
||||
sleep 1 # wait for backend to exit
|
||||
pid=$(pgrep -f clixon_backend)
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ $expecterr -eq 1 ]; then
|
||||
|
|
@ -72,6 +77,7 @@ testrun(){
|
|||
fi
|
||||
err
|
||||
fi
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
|
||||
|
|
@ -79,8 +85,8 @@ testrun(){
|
|||
err "Expected error"
|
||||
fi
|
||||
|
||||
# Get uid now, and compare with expected user
|
||||
u=$(ps -p $pid -uh | awk '{print $1}')
|
||||
# Get uid now, and compare with expected user (tail to skip hdr)
|
||||
u=$(ps -p $pid -u | tail -1 | awk '{print $1}')
|
||||
if [ $u != $expectuser ]; then
|
||||
err "$expectuser but user is $u"
|
||||
fi
|
||||
|
|
@ -116,7 +122,7 @@ testrun root root root drop_perm 0
|
|||
new "Start as root, set user but dont drop (expect still root)"
|
||||
testrun root $BUSER root none 0
|
||||
|
||||
new "Start as non-privileged, try to drop"
|
||||
new "Start as non-privileged, try to drop (but fail)"
|
||||
testrun $(whoami) $BUSER $BUSER drop_perm 1
|
||||
|
||||
sudo rm -rf $dir
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ if [ $BE -ne 0 ]; then
|
|||
start_backend -s init -f $cfg -- -s
|
||||
fi
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
|
||||
new "kill old restconf daemon"
|
||||
sudo pkill -u $wwwuser clixon_restconf
|
||||
|
||||
|
|
@ -55,7 +58,6 @@ new "start restconf daemon"
|
|||
start_restconf -f $cfg
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
wait_restconf
|
||||
|
||||
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||
|
|
|
|||
|
|
@ -217,10 +217,10 @@ if [ -z "$match" ]; then
|
|||
err "$expect" "$ret"
|
||||
fi
|
||||
nr=$(echo "$ret" | grep -c "data:")
|
||||
if [ $nr -lt 3 -o $nr -gt 4 ]; then
|
||||
#if [ $nr -lt 3 -o $nr -gt 4 ]; then
|
||||
if [ $nr -lt 3 ]; then
|
||||
err 4 "$nr"
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
|
||||
# 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications
|
||||
|
|
@ -232,7 +232,8 @@ if [ -z "$match" ]; then
|
|||
err "$expect" "$ret"
|
||||
fi
|
||||
nr=$(echo "$ret" | grep -c "data:")
|
||||
if [ $nr -lt 4 -o $nr -gt 10 ]; then
|
||||
#if [ $nr -lt 4 -o $nr -gt 10 ]; then
|
||||
if [ $nr -lt 4 ]; then
|
||||
err 6 "$nr"
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -198,14 +198,14 @@ testrange(){
|
|||
post=$4
|
||||
|
||||
if [ $t = "string" ]; then # special case for string type error msg
|
||||
len=$(echo -n "$eval" | wc -c)
|
||||
len=$(echo -n "$eval" | wc -c | awk '{print $1}'; )
|
||||
errmsg="String length $len out of range: 1$post - 10$post, 14$post - 20$post"
|
||||
else
|
||||
errmsg="Number $eval$post out of range: 1$post - 10$post, 14$post - 20$post"
|
||||
fi
|
||||
|
||||
new "generated cli set $t leaf invalid"
|
||||
expectfn "$clixon_cli -1f $cfg -l o set l$t $eval" 255 "$errmsg";
|
||||
expectpart "$(clixon_cli -1f $cfg -l o set l$t $eval)" 255 "$errmsg"
|
||||
|
||||
new "generated cli set $t leaf OK"
|
||||
expectfn "$clixon_cli -1f $cfg -l o set l$t $val" 0 '^$'
|
||||
|
|
@ -219,7 +219,7 @@ testrange(){
|
|||
# (SAME AS FIRST ^)
|
||||
new "generated cli set $t leaf invalid"
|
||||
expectfn "$clixon_cli -1f $cfg -l o set l$t $eval" 255 "$errmsg"
|
||||
|
||||
|
||||
new "manual cli set $t leaf OK"
|
||||
expectfn "$clixon_cli -1f $cfg -l o man h$t $val" 0 '^$'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue